diff --git a/go/analysis/passes/printf/testdata/src/a/a.go b/go/analysis/passes/printf/testdata/src/a/a.go index b73d1cad..40e8f2f7 100644 --- a/go/analysis/passes/printf/testdata/src/a/a.go +++ b/go/analysis/passes/printf/testdata/src/a/a.go @@ -654,6 +654,32 @@ func dbg(format string, args ...interface{}) { 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 func externalPackage() { b.Wrapf("%s", 1) // want "Wrapf format %s has arg 1 of wrong type int" diff --git a/go/analysis/passes/printf/types.go b/go/analysis/passes/printf/types.go index 701d08be..28d94e1f 100644 --- a/go/analysis/passes/printf/types.go +++ b/go/analysis/passes/printf/types.go @@ -97,12 +97,21 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type, if t == argPointer { 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 { - return matchStructArgType(pass, t, str, arg, inProgress) + + under := typ.Elem().Underlying() + 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. + return t&argPointer != 0 } - // Check whether the rest can print pointers. - 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: return matchStructArgType(pass, t, typ, arg, inProgress)