Skip to content

Commit bcb5f28

Browse files
committed
tools/syz-linter: define context.Context usage rules
1. It is the first parameter everywhere except tests. 2. It is the second param in the tests. 3. It is always named ctx.
1 parent 8fc3779 commit bcb5f28

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

tools/syz-linter/linter.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func run(p *analysis.Pass) (any, error) {
6868
pass.checkStringLenCompare(n)
6969
case *ast.FuncDecl:
7070
pass.checkFuncArgs(n)
71+
pass.checkContextArgs(n)
7172
case *ast.CallExpr:
7273
pass.checkFlagDefinition(n)
7374
pass.checkLogErrorFormat(n)
@@ -229,6 +230,51 @@ func (pass *Pass) reportFuncArgs(fields []*ast.Field, first, last int) {
229230
pass.report(fields[first], "Use '%v %v'", names[2:], pass.typ(fields[first].Type))
230231
}
231232

233+
func (pass *Pass) checkContextArgs(n *ast.FuncDecl) {
234+
if n.Type.Params == nil {
235+
return
236+
}
237+
expectedArgPos := 0
238+
if len(n.Type.Params.List) > 0 {
239+
field := n.Type.Params.List[0]
240+
if strings.HasSuffix(pass.typ(field.Type), "*testing.T") {
241+
expectedArgPos = 1
242+
}
243+
}
244+
argPos := 0
245+
for _, field := range n.Type.Params.List {
246+
isContext := pass.typ(field.Type) == "context.Context"
247+
if len(field.Names) == 0 {
248+
if isContext {
249+
if argPos != expectedArgPos {
250+
if expectedArgPos == 0 {
251+
pass.report(field, "Context must be the first argument")
252+
} else {
253+
pass.report(field, "Context must be the second argument")
254+
}
255+
}
256+
pass.report(field, "Context variable must be named 'ctx'")
257+
}
258+
argPos++
259+
}
260+
for _, name := range field.Names {
261+
if isContext {
262+
if argPos != expectedArgPos {
263+
if expectedArgPos == 0 {
264+
pass.report(name, "Context must be the first argument")
265+
} else {
266+
pass.report(name, "Context must be the second argument")
267+
}
268+
}
269+
if name.Name != "ctx" && name.Name != "_" {
270+
pass.report(name, "Context variable must be named 'ctx'")
271+
}
272+
}
273+
argPos++
274+
}
275+
}
276+
}
277+
232278
func (pass *Pass) checkFlagDefinition(n *ast.CallExpr) {
233279
fun, ok := n.Fun.(*ast.SelectorExpr)
234280
if !ok {

tools/syz-linter/testdata/src/lintertest/lintertest.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package lintertest
55

66
import (
7+
"context"
78
"flag"
89
"fmt"
910
"log"
@@ -152,3 +153,24 @@ func anyInterface() interface{} { // want "Use any instead of interface{}"
152153
func(any) {} (y)
153154
return v
154155
}
156+
157+
func contextArgs(ctx context.Context) {
158+
}
159+
160+
func contextArgsBad1(c context.Context) { // want "Context variable must be named 'ctx'"
161+
}
162+
163+
func contextArgsBad2(a int, ctx context.Context) { // want "Context must be the first argument"
164+
}
165+
166+
func contextArgsBad4(ctx context.Context, a int) {
167+
}
168+
169+
func TestContextArgsGood(t *testing.T, ctx context.Context) {
170+
}
171+
172+
func TestContextArgsBad1(t *testing.T, c context.Context) { // want "Context variable must be named 'ctx'"
173+
}
174+
175+
func TestContextArgsBad2(t *testing.T, a int, ctx context.Context) { // want "Context must be the second argument"
176+
}

0 commit comments

Comments
 (0)