应使用类型断言加 ok 判断而非 switch node.(type),因后者易漏新字段;需检查 nil 字段再访问;AST 节点不可用 == 比较;获取真实类型须结合 go/types,不能仅靠 ast。

用类型断言加 ok 判断,别用 switch node.(type)
直接对 node 做 switch node.(type) 容易漏掉新版本节点(比如 Go 1.18 的 *ast.TypeSpec.TypeParams 或 Go 1.21 的 *ast.FieldList 新字段),而 ast.Inspect 遍历的是所有实现了 ast.Node 接口的结构体,不依赖你手动枚举类型。
正确写法是显式类型断言 + ok 检查:
if f, ok := node.(*ast.FuncDecl); ok {
// 处理函数声明
}
if c, ok := node.(*ast.CallExpr); ok {
// 处理调用表达式
}
if t, ok := node.(*ast.TypeSpec); ok {
// 处理类型定义
}
注意:ok 必须参与判断——如果只写 f := node.(*ast.FuncDecl),遇到非 *ast.FuncDecl 类型会 panic。
nil 字段必须先判空,否则直接访问会 panic
AST 中很多字段是可能为 nil 的指针,不是空切片。常见踩坑点包括:
立即学习“go语言免费学习笔记(深入)”;
-
f.Type.Params和f.Type.Results可能为nil,直接取.List就 panic -
f.Type.Params.List里每个*ast.Field的Type可能是*ast.Ellipsis(对应...参数),不是*ast.ArrayType -
FuncType.Params为nil时,Field.Type可能是nil,需额外判空
安全写法示例:
if f.Type.Params != nil {
for _, field := range f.Type.Params.List {
if field.Type != nil {
// 安全访问 field.Type
}
}
}
不能用 == 比较 AST 节点,也没法获取父节点
ast.Node 不实现 ==,所以 node == someNode 恒为 false;也不要试图靠地址比较来识别“同一个变量声明”——AST 是只读副本,节点地址无语义。
另外,ast.Inspect 回调里拿不到父节点、作用域或上下文信息。需要这类信息时,得自己维护栈:
- 进入
*ast.FuncDecl时 push 函数名 - 离开时 pop
- 在子节点回调中通过栈顶获取当前所在函数
别指望 Inspect 自带作用域推导——它只是语法遍历,不是语义分析。
想查真实类型?必须上 go/types,单靠 ast 不行
ast.Ident 节点里的 Obj 字段只指向一个符号声明(*ast.Object),不带类型信息。例如 buf := bytes.NewBuffer(...) 中的 buf,仅靠 AST 无法知道它的真实类型是 *bytes.Buffer。
要获得运行时类型、跨包引用、接口方法集等语义信息,必须结合 go/types:
- 用
parser.ParseFile得到*ast.File - 用
types.Config.Check对其做类型检查,产出*types.Info - 从
info.Types或info.Defs中查ast.Ident对应的types.Type
绕过 go/types 直接靠字符串匹配包名(如找 "exec.Command")会误判别名导入或本地同名函数——这是安全检测中最常见的漏报根源。


















