mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
go.tools/cmd/vet: verify printf for String and Error implementers
Fixes golang/go#5624. R=adonovan CC=gobot, golang-dev, gri https://golang.org/cl/9657048
This commit is contained in:
parent
221795b447
commit
3402cf10ab
26
cmd/vet/testdata/print.go
vendored
26
cmd/vet/testdata/print.go
vendored
@ -75,6 +75,8 @@ func PrintfTests() {
|
|||||||
fmt.Printf("%x %x %x %x", 3, i, "hi", s)
|
fmt.Printf("%x %x %x %x", 3, i, "hi", s)
|
||||||
fmt.Printf("%X %X %X %X", 3, i, "hi", s)
|
fmt.Printf("%X %X %X %X", 3, i, "hi", s)
|
||||||
fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
|
fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
|
||||||
|
fmt.Printf("%s", stringerv)
|
||||||
|
fmt.Printf("%T", stringerv)
|
||||||
fmt.Printf("%*%", 2) // Ridiculous but allowed.
|
fmt.Printf("%*%", 2) // Ridiculous but allowed.
|
||||||
// Some bad format/argTypes
|
// Some bad format/argTypes
|
||||||
fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type"
|
fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type"
|
||||||
@ -94,6 +96,7 @@ func PrintfTests() {
|
|||||||
fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type"
|
fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type"
|
||||||
fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type"
|
fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type"
|
||||||
fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type"
|
fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type"
|
||||||
|
fmt.Printf("%t", stringerv) // ERROR "arg stringerv for printf verb %t of wrong type"
|
||||||
fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
|
fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type"
|
||||||
fmt.Println() // not an error
|
fmt.Println() // not an error
|
||||||
fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call"
|
fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call"
|
||||||
@ -116,7 +119,7 @@ func PrintfTests() {
|
|||||||
const format = "%s %s\n"
|
const format = "%s %s\n"
|
||||||
Printf(format, "hi", "there")
|
Printf(format, "hi", "there")
|
||||||
Printf(format, "hi") // ERROR "wrong number of args for format in Printf call"
|
Printf(format, "hi") // ERROR "wrong number of args for format in Printf call"
|
||||||
f := new(File)
|
f := new(stringer)
|
||||||
f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call"
|
f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call"
|
||||||
f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call"
|
f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call"
|
||||||
f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb"
|
f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb"
|
||||||
@ -158,7 +161,28 @@ func Printf(format string, args ...interface{}) {
|
|||||||
panic("don't call - testing only")
|
panic("don't call - testing only")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printf is used by the test so we must declare it.
|
||||||
|
func printf(format string, args ...interface{}) {
|
||||||
|
panic("don't call - testing only")
|
||||||
|
}
|
||||||
|
|
||||||
// multi is used by the test.
|
// multi is used by the test.
|
||||||
func multi() []interface{} {
|
func multi() []interface{} {
|
||||||
panic("don't call - testing only")
|
panic("don't call - testing only")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type stringer float64
|
||||||
|
|
||||||
|
var stringerv stringer
|
||||||
|
|
||||||
|
func (*stringer) String() string {
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*stringer) Warn(int, ...interface{}) string {
|
||||||
|
return "warn"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*stringer) Warnf(int, string, ...interface{}) string {
|
||||||
|
return "warnf"
|
||||||
|
}
|
||||||
|
@ -57,11 +57,33 @@ func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
|
func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
|
||||||
// TODO: for now, we can only test builtin types and untyped constants.
|
// TODO: for now, we can only test builtin types, untyped constants, and Stringer/Errors.
|
||||||
typ := f.pkg.types[arg]
|
typ := f.pkg.types[arg]
|
||||||
if typ == nil {
|
if typ == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// If we can use a string and this is a named type, does it implement the Stringer or Error interface?
|
||||||
|
// TODO: Simplify when we have the IsAssignableTo predicate in go/types.
|
||||||
|
if named, ok := typ.(*types.Named); ok && t&argString != 0 {
|
||||||
|
for i := 0; i < named.NumMethods(); i++ {
|
||||||
|
method := named.Method(i)
|
||||||
|
// Method must be either String or Error.
|
||||||
|
if method.Name() != "String" && method.Name() != "Error" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sig := method.Type().(*types.Signature)
|
||||||
|
// There must be zero arguments and one return.
|
||||||
|
if sig.Params().Len() != 0 || sig.Results().Len() != 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Result must be string.
|
||||||
|
if !isUniverseString(sig.Results().At(0).Type()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// It's a Stringer and we can print it.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
basic, ok := typ.Underlying().(*types.Basic)
|
basic, ok := typ.Underlying().(*types.Basic)
|
||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
@ -161,9 +183,10 @@ func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// It must have return type "string" from the universe.
|
// It must have return type "string" from the universe.
|
||||||
result := sig.Results().At(0).Type()
|
return isUniverseString(sig.Results().At(0).Type())
|
||||||
if types.IsIdentical(result, types.Typ[types.String]) {
|
}
|
||||||
return true
|
|
||||||
}
|
// isUniverseString reports whether the type is the predeclared type "string".
|
||||||
return false
|
func isUniverseString(typ types.Type) bool {
|
||||||
|
return types.IsIdentical(typ, types.Typ[types.String])
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user