go的ast包仅做语法解析,不提供类型信息,故*ast.ident.obj为nil;需配合go/types进行类型检查,将类型信息注入ast节点。

为什么直接用 ast.ParseFile 会漏掉类型信息
Go 的 ast 包本身只做语法树解析,不涉及语义——它不认识 int 是内置类型、time.Time 是哪个包的、甚至分不清变量声明和函数调用里的同名标识符。如果你只调用 ast.ParseFile,得到的 *ast.File 里所有 *ast.Ident 的 Obj 字段都是 nil,后续想查「这个 err 是不是来自 errors.New」就无从下手。
真正需要的是 go/types 配合 ast:先用 ast.ParseFile 构建语法树,再用 types.Checker 做一遍类型检查,把类型信息“注入”回 AST 节点的 Obj 和 Type 字段中。
- 必须用
parser.ParseFile(而非ast.ParseFile)配合token.FileSet,否则types.Checker无法定位位置 -
types.Config.Check的第二个参数要传入原始*ast.File切片,不能传单个文件指针后反复复用 - 类型检查后,
ast.Inspect遍历时才能安全读取ident.Obj和call.Fun.Type()
如何安全提取函数调用链(比如找所有 http.HandleFunc)
别直接比对 *ast.CallExpr.Fun 的字符串名——Go 支持别名导入、嵌套调用(如 (http.DefaultServeMux).HandleFunc)、甚至类型断言后的调用,硬匹配会漏。
正确做法是结合 types.Info 中的 Types 映射,拿到调用表达式的实际类型,再用 types.TypeString 或 types.IsInterface 等判断是否匹配目标签名:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
if call, ok := node.(*ast.CallExpr); ok {
if sig, ok := info.Types[call.Fun].Type.(*types.Signature); ok {
if recv := sig.Recv(); recv != nil {
// 处理方法调用
}
// 检查参数数量、返回值类型等
}
}
- 优先用
info.Types[expr].Type而非expr.String()做判定 - 对
http.HandleFunc这类导出函数,需确认其包路径是"net/http",可用types.TypeString(sig.Recv().Type(), nil)辅助判断接收者 - 注意
info.Types的 key 是ast.Expr节点指针,不是节点内容,所以必须在Inspect遍历中实时查
为什么 ast.Inspect 里修改节点不生效
ast.Inspect 是只读遍历,传入的 node 是当前节点的副本指针,直接赋值(如 node = &ast.BadExpr{})只改局部变量,不影响原树。真要重写 AST,得用 ast.NodeVisitor 实现 Visit 方法,并按规则返回替换后的节点。
- 若只是收集信息,用
ast.Inspect足够,性能更好 - 若要生成新代码(如自动加日志、删 dead code),必须用
astutil.Apply或手写Visit,且注意返回值语义:nil表示跳过子树,node表示保留,新节点表示替换 - 修改后需用
format.Node输出,不能直接fmt.Println—— 后者不格式化,也不处理位置信息
如何处理 vendor 和 go mod 下的多包依赖分析
单纯解析单个文件毫无意义——静态分析必须知道符号定义在哪。用 go list -json 获取完整包列表,再用 loader.Config(或新版 golang.org/x/tools/go/packages)加载所有包,才能让 types.Checker 正确解析跨包引用。
- 别手动拼
GOROOT和GOPATH,用packages.Load自动处理模块模式、vendor 目录、replace 指令 - 设置
packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax,缺一不可:没有NeedDeps就看不到依赖包的 AST,没有NeedTypes就拿不到类型信息 - 错误处理要细粒度:检查
pkg.Errors(解析错误)和pkg.TypeCheckErrors(类型错误),二者来源不同,修复方式也不同
最常被忽略的是包加载时的构建约束(build tags)和环境变量(如 GOOS=js)。不显式传入 Env 和 BuildFlags,分析结果可能和真实构建行为不一致。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










