Skip to content

Commit 9ef93f7

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 9ef93f7

File tree

2 files changed

+75
-13
lines changed

2 files changed

+75
-13
lines changed

tools/syz-linter/linter.go

Lines changed: 38 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,43 @@ 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+
pass.report(field, "Context must be the first argument")
251+
}
252+
pass.report(field, "Context variable must be named 'ctx'")
253+
}
254+
argPos++
255+
}
256+
for _, name := range field.Names {
257+
if isContext {
258+
if argPos != expectedArgPos {
259+
pass.report(name, "Context must be the first argument")
260+
}
261+
if name.Name != "ctx" && name.Name != "_" {
262+
pass.report(name, "Context variable must be named 'ctx'")
263+
}
264+
}
265+
argPos++
266+
}
267+
}
268+
}
269+
232270
func (pass *Pass) checkFlagDefinition(n *ast.CallExpr) {
233271
fun, ok := n.Fun.(*ast.SelectorExpr)
234272
if !ok {

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

Lines changed: 37 additions & 13 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"
@@ -28,14 +29,16 @@ func stringComparison() {
2829

2930
func returnString() string { return "foo" }
3031

31-
//
3232
// One space.
33-
// Two spaces.
34-
// One tab.
35-
// Two tabs.
36-
//No space. // want "Use either //<one-or-more-spaces>comment or //<one-or-more-tabs>comment format for comments"
33+
//
34+
// Two spaces.
35+
// One tab.
36+
// Two tabs.
37+
//
38+
// No space. // want "Use either //<one-or-more-spaces>comment or //<one-or-more-tabs>comment format for comments"
39+
//
3740
// Tab and spaces. // want "Use either //<one-or-more-spaces>comment or //<one-or-more-tabs>comment format for comments"
38-
// Space and tab. // want "Use either //<one-or-more-spaces>comment or //<one-or-more-tabs>comment format for comments"
41+
// Space and tab. // want "Use either //<one-or-more-spaces>comment or //<one-or-more-tabs>comment format for comments"
3942
func checkCommentSpace() {
4043
checkCommentSpace() // lower-case comment is OK
4144
// Capital letter comment.
@@ -126,13 +129,13 @@ func varDecls() {
126129

127130
func minmax() {
128131
x, y := 0, 0
129-
if x < y + 1 { // want "Use max function instead"
132+
if x < y+1 { // want "Use max function instead"
130133
x = y + 1
131134
}
132-
if x >= y { // want "Use max function instead"
135+
if x >= y { // want "Use max function instead"
133136
y = x
134137
}
135-
if x > 10 { // want "Use min function instead"
138+
if x > 10 { // want "Use min function instead"
136139
x = 10
137140
}
138141
}
@@ -145,10 +148,31 @@ func loopvar() {
145148
}
146149
}
147150

148-
func anyInterface() interface{} { // want "Use any instead of interface{}"
149-
var v interface{} // want "Use any instead of interface{}"
150-
func(interface{}) {} (v) // want "Use any instead of interface{}"
151+
func anyInterface() interface{} { // want "Use any instead of interface{}"
152+
var v interface{} // want "Use any instead of interface{}"
153+
func(interface{}) {}(v) // want "Use any instead of interface{}"
151154
var y any
152-
func(any) {} (y)
155+
func(any) {}(y)
153156
return v
154157
}
158+
159+
func contextArgs(ctx context.Context) {
160+
}
161+
162+
func contextArgsBad1(c context.Context) { // want "Context variable must be named 'ctx'"
163+
}
164+
165+
func contextArgsBad2(a int, ctx context.Context) { // want "Context must be the first argument"
166+
}
167+
168+
func contextArgsBad4(ctx context.Context, a int) {
169+
}
170+
171+
func TestContextArgsGood(t *testing.T, ctx context.Context) {
172+
}
173+
174+
func TestContextArgsBad1(t *testing.T, c context.Context) { // want "Context variable must be named 'ctx'"
175+
}
176+
177+
func TestContextArgsBad2(t *testing.T, a int, ctx context.Context) { // want "Context must be the first argument"
178+
}

0 commit comments

Comments
 (0)