go 的 ast 包仅处理已有 ast 节点,解析需用 parser.parsefile 等函数并传入 token.fileset;遍历应使用 ast.inspect 或 visitor 避免 panic;修改后须用 go/format.node 格式化整个 *ast.file 以确保正确性。

Go 的 ast 包不直接“解析源码”,它只处理已有的 AST 节点;真正做词法+语法解析的是 parser.ParseFile 等函数,ast 本身是结果容器和遍历工具。
怎么拿到一个合法的 *ast.File
必须先调用 parser.ParseFile(或 parser.ParseDir),传入 *token.FileSet 和文件路径/内容。漏掉 *token.FileSet 会导致位置信息全为 0,后续定位错误或生成 patch 会失效。
-
fs := token.NewFileSet()是必需的,所有解析都依赖它记录行号列号 - 从磁盘读:
parser.ParseFile(fs, "main.go", nil, parser.ParseComments) - 从字符串读:需用
fs.AddFile注册虚拟文件,再传入字节切片 - 忽略注释时去掉
parser.ParseComments,但ast.CommentGroup字段就全为 nil
如何安全遍历 AST 而不 panic
直接类型断言 node.(*ast.CallExpr) 在节点类型不匹配时会 panic;应始终用 ast.Inspect 或自定义 ast.Visitor,并配合类型检查。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
ast.Inspect是最简方式:回调函数返回true继续遍历,false跳过子树 - 若需区分节点类型,用
switch n := node.(type),而非多个if n, ok := node.(*ast.X) - 注意
ast.File的Decls是[]ast.Decl,不是所有 decl 都有Name字段(比如*ast.GenDecl下的Specs才有) - 访问
FuncLit.Body前先确认非 nil,空函数体(func() {})的Body是*ast.BlockStmt,但func()声明没有Body
修改 AST 后怎么写回源码
ast 包不提供“反解析”能力,不能把改过的 AST 直接转成格式良好的 Go 源码;必须用 go/format.Node 或 gofmt 工具链。
-
format.Node(w, fset, node)可将任意节点(如*ast.File)格式化输出到io.Writer - 输出前确保
fset和原解析时一致,否则位置混乱、//line注释错位 - 若只改局部(如某个
*ast.AssignStmt),直接 format 它会丢失上下文缩进;稳妥做法是 format 整个*ast.File - 不要手动拼接字符串生成代码——缩进、括号、换行、注释位置极易出错
真正难的不是遍历或改节点,而是理解 Go 语法树中哪些字段可为空、哪些隐含约束(比如 ast.IfStmt 的 Init 是可选语句,但 Else 是 ast.Stmt,也可能是 *ast.IfStmt 形成嵌套)。没跑通一个真实 case 前,别信文档里的“典型结构”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










