diff --git a/pkg/linters/ctxbackground/ctxbackground.go b/pkg/linters/ctxbackground/ctxbackground.go index 495de323ecf..547c4ca51ed 100644 --- a/pkg/linters/ctxbackground/ctxbackground.go +++ b/pkg/linters/ctxbackground/ctxbackground.go @@ -40,12 +40,17 @@ func run(pass *analysis.Pass) (any, error) { continue } - for encl := range cur.Enclosing((*ast.FuncDecl)(nil)) { - fn, ok := encl.Node().(*ast.FuncDecl) - if !ok { + for encl := range cur.Enclosing((*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) { + var ftype *ast.FuncType + switch fn := encl.Node().(type) { + case *ast.FuncDecl: + ftype = fn.Type + case *ast.FuncLit: + ftype = fn.Type + default: continue } - ctxParamName, ok := contextParamName(pass, fn) + ctxParamName, ok := contextParamName(pass, ftype) if !ok { break } @@ -95,15 +100,15 @@ func isContextBackgroundCall(pass *analysis.Pass, call *ast.CallExpr) bool { } // contextParamName returns the first non-blank context.Context parameter name. -func contextParamName(pass *analysis.Pass, fn *ast.FuncDecl) (string, bool) { - if fn.Type.Params == nil { +func contextParamName(pass *analysis.Pass, ftype *ast.FuncType) (string, bool) { + if ftype == nil || ftype.Params == nil { return "", false } ctxType := contextType(pass) if ctxType == nil { return "", false } - for _, field := range fn.Type.Params.List { + for _, field := range ftype.Params.List { t := pass.TypesInfo.TypeOf(field.Type) if t == nil { continue diff --git a/pkg/linters/ctxbackground/testdata/src/ctxbackground/ctxbackground.go b/pkg/linters/ctxbackground/testdata/src/ctxbackground/ctxbackground.go index f7a30cca7e3..ba8a15b9a12 100644 --- a/pkg/linters/ctxbackground/testdata/src/ctxbackground/ctxbackground.go +++ b/pkg/linters/ctxbackground/testdata/src/ctxbackground/ctxbackground.go @@ -41,3 +41,21 @@ func DoWorkShadowedContext(ctx context.Context) { _ = context.Background() _ = ctx } + +// flagged: closure whose own signature receives ctx +func RegisterHandler() { + handle(func(ctx context.Context) { + _ = context.Background() // want `use the context.Context parameter instead of context.Background\(\)` + }) +} + +// not flagged: closure with no ctx param nested inside a function that does receive ctx; +// the nearest enclosing function (the closure) has no ctx, so we stop there. +func DoWorkWithDetachedClosure(ctx context.Context) { + go func() { + _ = context.Background() + }() + _ = ctx +} + +func handle(f func(context.Context)) { f(nil) } diff --git a/pkg/linters/ctxbackground/testdata/src/ctxbackground/ctxbackground.go.golden b/pkg/linters/ctxbackground/testdata/src/ctxbackground/ctxbackground.go.golden index 3734e484b3d..8e57fcf0faf 100644 --- a/pkg/linters/ctxbackground/testdata/src/ctxbackground/ctxbackground.go.golden +++ b/pkg/linters/ctxbackground/testdata/src/ctxbackground/ctxbackground.go.golden @@ -41,3 +41,21 @@ func DoWorkShadowedContext(ctx context.Context) { _ = context.Background() _ = ctx } + +// flagged: closure whose own signature receives ctx +func RegisterHandler() { + handle(func(ctx context.Context) { + _ = ctx // want `use the context.Context parameter instead of context.Background\(\)` + }) +} + +// not flagged: closure with no ctx param nested inside a function that does receive ctx; +// the nearest enclosing function (the closure) has no ctx, so we stop there. +func DoWorkWithDetachedClosure(ctx context.Context) { + go func() { + _ = context.Background() + }() + _ = ctx +} + +func handle(f func(context.Context)) { f(nil) }