go tool compile -S 输出的是 Go 编译器 SSA 后端生成的 Plan 9 伪汇编,含源码行号和变量名,但非真实机器码;查看 AST 需用 gofmt -ast 或 go/parser+go/ast。

用 go tool compile -S 看不到 AST,得换工具
很多人试过 go tool compile -S,发现输出的是汇编,不是 AST。AST 是语法树,不是机器码,必须用专门解析源码的工具。Go 标准库自带 go/ast 和 go/parser,但直接写代码看树太重;更轻量的方式是用 go/printer 配合 go/parser 打印结构,或者用现成命令行工具。
推荐方案:用 gofmt -ast 快速查看
gofmt 内置了 AST 输出能力,不需额外安装,适合快速验证。它会把源文件解析成 AST 并以 Go 代码风格打印节点结构,可读性比原始 reflect 输出好得多。
- 对单个文件:运行
gofmt -ast main.go - 只看某函数(比如
main):先用go list -f '{{.GoFiles}}' .确认文件名,再配合grep过滤输出中的FuncDecl节点 - 注意:
-ast不格式化代码,只是解析+打印,输入文件无需可编译(比如有语法错误时会报错退出)
调试复杂结构时,用 ast.Print 写小脚本
当需要过滤节点、查看字段细节(比如某个 CallExpr 的 Fun 是什么类型),就得自己写解析脚本。核心是三步:调用 parser.ParseFile → 得到 *ast.File → 用 ast.Print 或遍历节点。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 示例脚本片段:
file, err := parser.ParseFile(fset, "main.go", nil, parser.AllErrors) if err != nil { log.Fatal(err) } ast.Print(fset, file) -
fset(*token.FileSet)必须传,否则位置信息为空,ast.Print会 panic - 若想高亮某个节点类型,别直接改
ast.Print,而是用ast.Inspect遍历,在回调里判断node.Kind() == reflect.TypeOf(&ast.CallExpr{}).Elem().Kind()这类逻辑(实际用类型断言更稳)
常见陷阱:AST 节点字段名和实际结构不一致
比如 ast.IfStmt 的 Init 字段只有在 if v := x; cond { } 形式下才非 nil;普通 if cond { } 中 Init 是 nil,不是空语句节点。又比如 ast.CallExpr 的 Fun 是表达式,不是函数名字符串——它可能是 *ast.Ident(如 fmt.Println),也可能是 *ast.SelectorExpr(如 a.Method),甚至 *ast.ParenExpr。
- 不要假设字段一定有值,所有指针字段都要判空
-
go/ast的结构是“抽象”语法树,省略了部分 token(如分号、括号),也不保留注释(除非显式启用parser.ParseComments) - 如果需要注释或更细粒度 token 信息,得用
go/token+go/scanner,而不是只依赖go/ast
AST 结构本身不难查,难点在于 Go 的 AST 节点设计偏向编译器内部使用,字段语义和常见直觉有偏差,比如 Body 在 FuncDecl 里是 *ast.BlockStmt,但在 IfStmt 里却是 *ast.BlockStmt 或 nil,没有统一 wrapper。动手前先用 gofmt -ast 看一眼真实结构,比硬猜快得多。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










