解析go源文件必须先创建token.newfileset()并传给parser.parsefile,否则会panic;用ast.inspect遍历ast,注意funcdecl与funclit区别,assignstmt与valuespec差异,避免nil panic和语义误判。

用 go/ast 解析 Go 源文件得到 AST 根节点
直接调用 parser.ParseFile 是最常用起点,但必须传入有效的 *token.FileSet,否则后续所有位置信息(如行号)会失效或 panic。别跳过这步初始化。
常见错误现象:panic: runtime error: invalid memory address,往往是因为传了 nil *token.FileSet 给 parser.ParseFile。
- 先创建
token.NewFileSet(),再传给parser.ParseFile - 文件路径要用绝对路径,相对路径可能因工作目录不同导致
os.Open失败 - 如果只是分析字符串内容(比如从 IDE 插件来的一段代码),用
parser.ParseExpr或parser.ParseFile配合token.NewFileSet().AddFile手动注册虚拟文件
遍历 AST 时用 ast.Inspect 而不是手写递归
ast.Inspect 内置了安全的深度优先遍历逻辑,自动跳过 nil 子节点,也支持中途中断(返回 false)。手写递归容易漏掉字段(比如 FuncType.Params 和 FuncType.Results 都是 *ast.FieldList,但内部结构不同),还可能触发 nil panic。
使用场景:提取所有函数名、找某个变量的赋值位置、统计 interface 方法数。
- 回调函数签名必须是
func(node ast.Node) bool - 在回调里用
ast.IsExported判断标识符是否导出,别直接比对首字母大小写 - 遇到
*ast.Ident时,用node.(*ast.Ident).Name取名字,但要先断言类型,ast.Inspect传进来的是ast.Node接口
识别函数定义得同时检查 *ast.FuncDecl 和 *ast.FuncLit
顶层函数是 *ast.FuncDecl,匿名函数字面量是 *ast.FuncLit,二者结构差异大:前者有 Name 字段,后者只有 Type 和 Body。只查一种会漏掉闭包或 map value 中的函数。
参数差异:*ast.FuncDecl 的 Recv 字段非空表示方法;*ast.FuncLit 的 Func 字段恒为 &ast.FuncType{...},没有接收者。
- 判断是否为方法:检查
funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 - 获取函数参数名:遍历
funcType.Params.List[i].Names,每个*ast.Field可能含多个*ast.Ident(如a, b int) - 注意
funcLit.Type.Params和funcLit.Type.Results也是*ast.FieldList,需同样展开
提取变量赋值时小心 *ast.AssignStmt 和 *ast.ValueSpec 的区别
短变量声明(:=)属于 *ast.AssignStmt,而 var x int = 1 这类是 *ast.ValueSpec(在 *ast.GenDecl 的 Specs 里)。两者 AST 节点类型完全不同,不能混用断言。
性能影响:*ast.ValueSpec 的 Names 是 []*ast.Ident,可直接取名;*ast.AssignStmt 的 Lhs 是 []ast.Expr,需逐个断言为 *ast.Ident 或 *ast.IndexExpr(如切片赋值)。
- 短声明的右值在
AssignStmt.Rhs,可能是任意表达式,包括函数调用、字面量、复合字面量 -
var声明若无初始值(如var x int),ValueSpec.Values为 nil,别直接遍历 - 批量声明如
var a, b = 1, "hello",Values长度可能小于Names,需按索引安全访问
FuncDecl.Recv,而接口方法签名里的接收者根本不存在。不对照 go/ast 文档反复验证字段含义,很容易误判。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











