应使用 ast.Inspect 结合 go/types 分析函数体,识别“调用后未检查 error 即使用返回值”的可疑模式,需确认 error 类型来源、检查是否显式判断 err、排除安全场景,并处理多返回值、包装错误及 goroutine 内错误等边界情况。

用 ast.Inspect 遍历函数体,找“调用后没跟 if err != nil”的模式
Go 的 error 是值,不检查就用返回值是常见 panic 根源。AST 分析不能运行代码,但能识别典型未处理模式:比如 expr, err := someFunc() 后直接出现 use(expr),中间没 if 判断。重点不是“有没有 err 变量”,而是“err 是否被显式检查过”。
- 只匹配
:=或=赋值语句中含err标识符(不强制叫err,但绝大多数项目遵循此命名) - 从该赋值节点开始,向上查找最近的同级或父级
if语句,检查其条件是否为err != nil或err == nil - 若赋值后下一个非声明类语句是函数调用、下标访问、解引用等(即可能触发 panic 的操作),且中间无 if 检查,则标记为可疑
- 注意跳过已知安全场景:如
_, err := os.Open(...)(明确丢弃结果)、log.Fatal(err)(终止流程)、return err(错误透传)
go/ast 中怎么定位“error 类型的返回值”
不能只靠变量名叫 err 就认定它是 error——得确认它真来自一个返回 error 的函数调用。这需要类型信息,纯 AST 不够,必须结合 go/types。
- 先用
ast.Inspect找到所有调用表达式CallExpr - 对每个
CallExpr,用types.Info.Types[callExpr].Type获取其完整返回类型(通常是*types.Tuple) - 检查该元组最后一个元素的类型是否满足
types.IsInterface且其方法集包含Error() string - 若满足,再看它是否被赋值给某个局部变量(即存在
AssignStmt或DeclStmt捕获了这个返回值)
为什么不用 go vet 或 staticcheck 就要自己写 AST 分析
现有工具覆盖有限:go vet 不检查 error 处理逻辑,staticcheck 的 SA5011 只报“未使用的变量”,不管变量是否该被检查;它们都不做控制流分析,无法判断“err 被赋值后,下一行是不是在解引用前一个零值”。
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
-
staticcheck会放过data, err := json.Marshal(v); fmt.Println(string(data))—— 它只看到data被用了,却不知道err没判,而data在失败时是nil - 真正要捕获的是“错误路径未隔离”,这需要模拟执行路径:赋值 → 条件分支缺失 → 危险操作
- 自己写 AST + types 分析虽重,但能精确控制规则边界,比如只检查 HTTP handler 函数、忽略 test 文件、跳过
//nolint:errcheck
容易漏掉的三种真实 case
人工 review 都容易跳过,AST 分析器若不特别处理也会漏掉:
立即学习“go语言免费学习笔记(深入)”;
- 多返回值中 error 不在最后:如
func() (int, error, string),此时err可能被赋给第二个变量,但开发者仍习惯写if err != nil,AST 需按实际位置索引,不能硬写[1] - error 被包装后传递:
return fmt.Errorf("wrap: %w", err),原err虽被检查,但包装后的 error 没被检查——这不是 bug,但若分析目标是“所有 error 返回值都需处理”,就得穿透包装链找源头 - goroutine 内部错误:如
go func() { _, err := do(); if err != nil { log.Print(err) } }(),err 在 goroutine 内检查了,但主 goroutine 完全不知情——AST 分析需识别 go 语句块并单独建模其错误流
这些都不是语法错误,却是线上 panic 的高频来源。写分析器时,别只盯着“有没有 if”,得盯住“控制流是否真的把错误路径和成功路径隔开了”。

















