Summary
ctxbackground flags context.Background() when the enclosing function already receives a context.Context parameter. To find that parameter it walks ancestors with cur.Enclosing((*ast.FuncDecl)(nil)), which matches only top-level function declarations and silently steps over any intervening *ast.FuncLit (closure). As a result the linter attributes a Background() call to the wrong function scope. This is the same scope-boundary class as #40947 (wgdonenotdeferred), but in a distinct linter and with the opposite root cause: instead of walking too far, it considers too few node kinds.
Location
pkg/linters/ctxbackground/ctxbackground.go:43-71:
for encl := range cur.Enclosing((*ast.FuncDecl)(nil)) { // <-- FuncLit not in filter
fn, ok := encl.Node().(*ast.FuncDecl)
...
ctxParamName, ok := contextParamName(pass, fn)
if !ok {
break
}
pass.Report(...)
break
}
contextParamName only accepts *ast.FuncDecl (pkg/linters/ctxbackground/ctxbackground.go:98), so a closure's own parameter list is never examined.
Impact
False negative — the primary, clean case. A Background() call inside a closure whose own signature receives a context is missed, because the walk jumps past the closure to the outer FuncDecl:
func register(mux *http.ServeMux) { // no ctx param
mux.HandleFunc("/", func(ctx context.Context) {
_ = context.Background() // NOT flagged: should be (this func receives ctx)
})
}
The nearest enclosing function (the FuncLit) does receive ctx — exactly the linter's target — yet Enclosing((*ast.FuncDecl)(nil)) resolves to register, which has no ctx, so the loop breaks without reporting.
Secondary — suspect autofix in detached closures. The inverse shape flags the call and rewrites it to the outer captured ctx:
func serve(ctx context.Context) {
go func() {
_ = context.Background() // flagged -> suggests replacing with `ctx`
}()
}
Substituting the request-scoped ctx into a goroutine that deliberately detached from it can be semantically wrong (the goroutine may outlive the request and must not be cancelled with it). The linter applies the fix without considering the FuncLit boundary or whether the closure even intends to capture that ctx.
Why the test gap hid it
testdata/src/ctxbackground/ctxbackground.go covers free functions, methods, blank _ params, init, and shadowing — but contains no FuncLit/closure cases at all. The boundary behavior is entirely untested.
Recommendation
Resolve the nearest enclosing function scope, FuncDecl or FuncLit, and inspect that node's own parameter list. Generalize contextParamName to take an *ast.FuncType (both FuncDecl.Type and FuncLit.Type are *ast.FuncType):
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
}
ctxParamName, ok := contextParamName(pass, ftype)
if !ok {
break // nearest function scope has no ctx -> stop
}
pass.Report(...)
break
}
Stopping at the nearest function scope (rather than continuing outward) keeps the diagnostic anchored to a parameter the call site can actually reference, and naturally excludes detached closures that take no ctx of their own.
Validation checklist
Effort: small–medium (single-file; widen the Enclosing filter + change one helper signature + testdata).
References: §28076275497
Generated by 🤖 Sergo - Serena Go Expert · 302.6 AIC · ⌖ 11.3 AIC · ⊞ 5.9K · ◷
Summary
ctxbackgroundflagscontext.Background()when the enclosing function already receives acontext.Contextparameter. To find that parameter it walks ancestors withcur.Enclosing((*ast.FuncDecl)(nil)), which matches only top-level function declarations and silently steps over any intervening*ast.FuncLit(closure). As a result the linter attributes aBackground()call to the wrong function scope. This is the same scope-boundary class as #40947 (wgdonenotdeferred), but in a distinct linter and with the opposite root cause: instead of walking too far, it considers too few node kinds.Location
pkg/linters/ctxbackground/ctxbackground.go:43-71:contextParamNameonly accepts*ast.FuncDecl(pkg/linters/ctxbackground/ctxbackground.go:98), so a closure's own parameter list is never examined.Impact
False negative — the primary, clean case. A
Background()call inside a closure whose own signature receives a context is missed, because the walk jumps past the closure to the outerFuncDecl:The nearest enclosing function (the
FuncLit) does receivectx— exactly the linter's target — yetEnclosing((*ast.FuncDecl)(nil))resolves toregister, which has no ctx, so the loopbreaks without reporting.Secondary — suspect autofix in detached closures. The inverse shape flags the call and rewrites it to the outer captured ctx:
Substituting the request-scoped
ctxinto a goroutine that deliberately detached from it can be semantically wrong (the goroutine may outlive the request and must not be cancelled with it). The linter applies the fix without considering the FuncLit boundary or whether the closure even intends to capture that ctx.Why the test gap hid it
testdata/src/ctxbackground/ctxbackground.gocovers free functions, methods, blank_params,init, and shadowing — but contains noFuncLit/closure cases at all. The boundary behavior is entirely untested.Recommendation
Resolve the nearest enclosing function scope, FuncDecl or FuncLit, and inspect that node's own parameter list. Generalize
contextParamNameto take an*ast.FuncType(bothFuncDecl.TypeandFuncLit.Typeare*ast.FuncType):Stopping at the nearest function scope (rather than continuing outward) keeps the diagnostic anchored to a parameter the call site can actually reference, and naturally excludes detached closures that take no ctx of their own.
Validation checklist
flaggedtestdata:Background()inside afunc(ctx context.Context){...}closure nested in a no-ctx outer function.not flaggedtestdata:Background()inside a no-param closure nested in a function that does receive ctx (confirm the chosen policy and document it).init/shadow cases green.Effort: small–medium (single-file; widen the Enclosing filter + change one helper signature + testdata).
References: §28076275497