go标准库提供完整ast支持,直接使用go/parser和go/ast即可解析源码获取结构化ast;parsefile是常用入口,需指定fset和src并推荐启用parsecomments;遍历用ast.inspect而非手动递归;修改ast需复制重建;格式化输出应使用go/format.node或printer.fprint;语义分析需结合go/types补全。

Go语言标准库自带完整的AST支持,不需要自己从零实现解析器或语法树节点定义——直接用 go/parser 和 go/ast 就够了,写几行代码就能拿到结构化的AST。
用 go/parser.ParseFile 读取源码并生成AST
这是最常用入口:传入文件路径或字节内容,返回 *ast.File 节点。注意它不处理导入路径解析,只做纯语法解析。
- 如果文件有语法错误,会返回非空的
err,且*ast.File可能部分有效(比如前几行正确) - 必须指定
src参数,哪怕只是空字符串;否则某些位置信息会丢失 - 推荐加
parser.ParseComments模式,否则注释不会出现在ast.File.Comments中
file, err := parser.ParseFile(fset, "main.go", src, parser.ParseComments)
if err != nil {
log.Fatal(err)
}
遍历AST时别直接递归 ast.Node,用 ast.Inspect
ast.Inspect 是官方推荐的遍历方式,自动处理所有节点类型和子节点顺序,比手写 switch 或 type assertion 更可靠。
- 回调函数返回
bool:返回false表示跳过该节点的子树(可用于剪枝) - 不要在回调里修改节点字段(如
n.Name),AST是只读结构;真要改用ast.Copy+ 重建 - 如果只关心某类节点(比如所有
ast.FuncDecl),用ast.Walk配合自定义 visitor 更清晰
ast.Inspect(file, func(n ast.Node) bool {
if f, ok := n.(*ast.FuncDecl); ok {
fmt.Printf("func %s\n", f.Name.Name)
}
return true
})
提取函数签名时小心 ast.FuncType 的参数/结果字段
ast.FuncType 的 Params 和 Results 都是 *ast.FieldList,不是简单切片——每个 Field 可能含多个名字(如 a, b int)或类型别名(如 func(int) (x, y error))。
-
Field.Names是标识符列表,为空表示匿名参数(如func(int)中的int) -
Field.Type才是真实类型节点,需进一步展开(比如*ast.StarExpr表示指针) - 结果参数若带名字(
(err error)),Field.Names非空;若无名((error)),则只有类型
生成AST后想输出Go代码?别手写格式化,用 go/format
手动拼接字符串极易出错(缩进、分号、括号匹配),go/format.Node 能复用 gofmt 的逻辑,保底可编译。
- 输入是任意
ast.Node,输出是[]byte,需自己转成字符串或写入文件 - 若节点不完整(比如缺 package 声明),
format.Node可能 panic,建议包裹 recover - 如果目标是生成新文件,更稳妥的是构造完整
*ast.File后用printer.Fprint控制缩进细节
真正难的从来不是建树,而是理解哪些 AST 节点对应你关心的语义边界——比如 interface 方法集、嵌入字段展开、类型别名的底层类型追溯,这些都不在原始 AST 里,得靠 go/types 补全。没上 types.Info 前,光看 ast 层容易误判。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











