Skip to content

Commit 24806f2

Browse files
committed
go/analysis: add tests check for calling *F methods in fuzz func
The only *F methods that are allowed to be called from the function passed to (*F).Fuzz are Named and Failed. This check looks for other methods and will report errors. Change-Id: I30ec546d11f9e9b38ee673c5a0e7cf0fde8ca922 Reviewed-on: https://go-review.googlesource.com/c/tools/+/390974 Trust: Suzy Mueller <[email protected]> Run-TryBot: Suzy Mueller <[email protected]> Reviewed-by: Hyang-Ah Hana Kim <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent dff7c5f commit 24806f2

File tree

2 files changed

+32
-9
lines changed

2 files changed

+32
-9
lines changed

go/analysis/passes/tests/testdata/src/a/go118_test.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,17 @@ func FuzzFunc(f *testing.F) {
2020
}
2121

2222
func FuzzFuncWithArgs(f *testing.F) {
23-
f.Add() // want `wrong number of values in call to \(\*testing.F\)\.Add: 0, fuzz target expects 2`
24-
f.Add(1, 2, 3, 4) // want `wrong number of values in call to \(\*testing.F\)\.Add: 4, fuzz target expects 2`
25-
f.Add(5, 5) // want `mismatched type in call to \(\*testing.F\)\.Add: int, fuzz target expects \[\]byte`
26-
f.Add([]byte("hello"), 5) // want `mismatched types in call to \(\*testing.F\)\.Add: \[\[\]byte int\], fuzz target expects \[int \[\]byte\]`
27-
f.Add(5, []byte("hello")) // OK
28-
f.Fuzz(func(t *testing.T, i int, b []byte) {}) // OK "arguments in func are allowed"
23+
f.Add() // want `wrong number of values in call to \(\*testing.F\)\.Add: 0, fuzz target expects 2`
24+
f.Add(1, 2, 3, 4) // want `wrong number of values in call to \(\*testing.F\)\.Add: 4, fuzz target expects 2`
25+
f.Add(5, 5) // want `mismatched type in call to \(\*testing.F\)\.Add: int, fuzz target expects \[\]byte`
26+
f.Add([]byte("hello"), 5) // want `mismatched types in call to \(\*testing.F\)\.Add: \[\[\]byte int\], fuzz target expects \[int \[\]byte\]`
27+
f.Add(5, []byte("hello")) // OK
28+
f.Fuzz(func(t *testing.T, i int, b []byte) { // OK "arguments in func are allowed"
29+
f.Add(5, []byte("hello")) // want `fuzz target must not call any \*F methods`
30+
f.Name() // OK "calls to (*F).Failed and (*F).Name are allowed"
31+
f.Failed() // OK "calls to (*F).Failed and (*F).Name are allowed"
32+
f.Fuzz(func(t *testing.T) {}) // want `fuzz target must not call any \*F methods`
33+
})
2934
}
3035

3136
func FuzzArgFunc(f *testing.F) {

go/analysis/passes/tests/tests.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ func checkFuzz(pass *analysis.Pass, fn *ast.FuncDecl) {
9999
// 4. Second argument onwards should be of type []byte, string, bool, byte,
100100
// rune, float32, float64, int, int8, int16, int32, int64, uint, uint8, uint16,
101101
// uint32, uint64
102+
// 5. func() must not call any *F methods, e.g. (*F).Log, (*F).Error, (*F).Skip
103+
// The only *F methods that are allowed in the (*F).Fuzz function are (*F).Failed and (*F).Name.
102104
// Returns the list of parameters to the fuzz function, if they are valid fuzz parameters.
103105
func checkFuzzCall(pass *analysis.Pass, fn *ast.FuncDecl) (params *types.Tuple) {
104106
ast.Inspect(fn, func(n ast.Node) bool {
@@ -121,7 +123,7 @@ func checkFuzzCall(pass *analysis.Pass, fn *ast.FuncDecl) (params *types.Tuple)
121123
// Argument should be a function
122124
if !argOk {
123125
pass.ReportRangef(expr, "argument to Fuzz must be a function")
124-
return true
126+
return false
125127
}
126128
// ff Argument function should not return
127129
if tSign.Results().Len() != 0 {
@@ -130,12 +132,28 @@ func checkFuzzCall(pass *analysis.Pass, fn *ast.FuncDecl) (params *types.Tuple)
130132
// ff Argument function should have 1 or more argument
131133
if tSign.Params().Len() == 0 {
132134
pass.ReportRangef(expr, "fuzz target must have 1 or more argument")
133-
return true
135+
return false
134136
}
135137
ok := validateFuzzArgs(pass, tSign.Params(), expr)
136138
if ok && params == nil {
137139
params = tSign.Params()
138140
}
141+
// Inspect the function that was passed as an argument to make sure that
142+
// there are no calls to *F methods, except for Name and Failed.
143+
ast.Inspect(expr, func(n ast.Node) bool {
144+
if call, ok := n.(*ast.CallExpr); ok {
145+
if !isFuzzTargetDot(pass, call, "") {
146+
return true
147+
}
148+
if !isFuzzTargetDot(pass, call, "Name") && !isFuzzTargetDot(pass, call, "Failed") {
149+
pass.ReportRangef(call, "fuzz target must not call any *F methods")
150+
}
151+
}
152+
return true
153+
})
154+
// We do not need to look at any calls to f.Fuzz inside of a Fuzz call,
155+
// since they are not allowed.
156+
return false
139157
}
140158
return true
141159
})
@@ -202,7 +220,7 @@ func isFuzzTargetDot(pass *analysis.Pass, call *ast.CallExpr, name string) bool
202220
if !isTestingType(pass.TypesInfo.Types[selExpr.X].Type, "F") {
203221
return false
204222
}
205-
if selExpr.Sel.Name == name {
223+
if name == "" || selExpr.Sel.Name == name {
206224
return true
207225
}
208226
}

0 commit comments

Comments
 (0)