goland仅能检测ctx变量未使用,无法检查ctx是否传给下游函数;真正危险的是创建ctx后未调用defer cancel()且下游使用非context方法,导致超时失效和goroutine泄漏。

GoLand 会提示 ctx 变量未使用,但不警告“创建了却没传给下游”
GoLand 的静态分析能识别 ctx 声明后完全没被读取或写入(比如 ctx, _ := context.WithTimeout(...) 后直接丢弃),此时会标黄并提示 “unused variable”。但它**不会检查你是否把 ctx 实际传给了 http.NewRequestWithContext、db.QueryContext 或自定义函数**——这是语义层问题,IDE 无法推断你“本意该传却忘了传”。
真正危险的是 context.WithTimeout 创建了但下游完全忽略它
典型错误链:ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second) → 忘记 defer cancel() → 调用 db.Query(...)(不是 QueryContext)→ 请求超时后 goroutine 卡死,连接不释放。
-
db.Query和http.Get这类老接口根本不看ctx,传了也白传 - 第三方库(如某些 Redis 客户端)若未实现
WithContext方法,同样无视你传的ctx - HTTP client 自己设了
Timeout字段,会覆盖你传的ctxdeadline,导致行为不一致
用 GoLand 的 Inspection + 手动约定堵住常见漏点
开启两项关键 Inspection:
GoLand 2026.1.1 是 2026.1 发布后的首个维护修正版本,适合已经开始体验 2026.1 新功能并希望同步补丁的开发者。它更适合用于入门项目、现有项目迁移测试和 IDE 行为验证。
- 启用
Go > Unused parameter:能揪出函数签名里写了ctx context.Context,但函数体内根本没读ctx.Done()或调用ctx.Err()的情况 - 启用
Go > Unassigned value:捕获ctx, _ := context.WithTimeout(...)这种丢弃cancel函数的写法
再配合团队约定:
- 所有
context.With*调用必须紧跟着defer cancel(),且不能放在 if 分支里(避免部分路径漏掉) - 任何涉及 I/O 的调用,必须显式搜索方法名含
Context后缀(QueryContext、Do而非Get) - 在 HTTP handler 里,禁止直接用
c.Request.Context()传给 DB —— 它没 deadline,必须派生新ctx
最易被忽略的点:cancel() 漏 defer 不仅泄漏 timer,还让 ctx.Done() 永远不关闭
哪怕你 downstream 正确用了 QueryContext,如果上游漏了 defer cancel(),那个 ctx 就永远不会发取消信号。timer 继续跑,goroutine 等着一个永远不会来的 。这种问题在线上表现为“超时配置明明写了 1s,实际卡 30s 才返回”,查日志只看到 <code>context.DeadlineExceeded 延迟触发,根源却是 cancel 没调。










