go/analysis/passes/printf: fix some pointer false positives

fmt's godoc reads:

	For compound objects, the elements are printed using these
	rules, recursively, laid out like this:

		struct:             {field0 field1 ...}
		array, slice:       [elem0 elem1 ...]
		maps:               map[key1:value1 key2:value2 ...]
		pointer to above:   &{}, &[], &map[]

That is, a pointer to a struct, array, slice, or map, can be correctly
printed by fmt if the type pointed to can be printed without issues.

vet was only following this rule for pointers to structs, omitting
arrays, slices, and maps. Fix that, and add tests for all the
combinations.

This change was originally made to cmd/vet in
https://go-review.googlesource.com/c/147758

Updates #27672.

Change-Id: I7e25ecaeed619ae8b6ada79bccacba6b67171733
Reviewed-on: https://go-review.googlesource.com/c/149318
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Alan Donovan 2018-11-13 13:19:12 -05:00
parent 150d8ac285
commit ea84011da2
2 changed files with 40 additions and 5 deletions

View File

@ -654,6 +654,32 @@ func dbg(format string, args ...interface{}) {
fmt.Printf(format, args...) fmt.Printf(format, args...)
} }
func PointersToCompoundTypes() {
stringSlice := []string{"a", "b"}
fmt.Printf("%s", &stringSlice) // not an error
intSlice := []int{3, 4}
fmt.Printf("%s", &intSlice) // want `Printf format %s has arg &intSlice of wrong type \*\[\]int`
stringArray := [2]string{"a", "b"}
fmt.Printf("%s", &stringArray) // not an error
intArray := [2]int{3, 4}
fmt.Printf("%s", &intArray) // want `Printf format %s has arg &intArray of wrong type \*\[2\]int`
stringStruct := struct{ F string }{"foo"}
fmt.Printf("%s", &stringStruct) // not an error
intStruct := struct{ F int }{3}
fmt.Printf("%s", &intStruct) // want `Printf format %s has arg &intStruct of wrong type \*struct{F int}`
stringMap := map[string]string{"foo": "bar"}
fmt.Printf("%s", &stringMap) // not an error
intMap := map[int]int{3: 4}
fmt.Printf("%s", &intMap) // want `Printf format %s has arg &intMap of wrong type \*map\[int\]int`
}
// Printf wrappers from external package // Printf wrappers from external package
func externalPackage() { func externalPackage() {
b.Wrapf("%s", 1) // want "Wrapf format %s has arg 1 of wrong type int" b.Wrapf("%s", 1) // want "Wrapf format %s has arg 1 of wrong type int"

View File

@ -97,12 +97,21 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
if t == argPointer { if t == argPointer {
return true return true
} }
// If it's pointer to struct, that's equivalent in our analysis to whether we can print the struct.
if str, ok := typ.Elem().Underlying().(*types.Struct); ok { under := typ.Elem().Underlying()
return matchStructArgType(pass, t, str, arg, inProgress) switch under.(type) {
} case *types.Struct: // see below
case *types.Array: // see below
case *types.Slice: // see below
case *types.Map: // see below
default:
// Check whether the rest can print pointers. // Check whether the rest can print pointers.
return t&argPointer != 0 return t&argPointer != 0
}
// If it's a pointer to a struct, array, slice, or map, that's
// equivalent in our analysis to whether we can print the type
// being pointed to.
return matchArgTypeInternal(pass, t, under, arg, inProgress)
case *types.Struct: case *types.Struct:
return matchStructArgType(pass, t, typ, arg, inProgress) return matchStructArgType(pass, t, typ, arg, inProgress)