解析器配置需检查四件事:fset非nil、文件名合理、mode含必要标志、src与读取方式匹配;AST遍历时须判空字段,优先用ast.Inspect而非手写递归;多分析场景用inspector提速且可控。

别用“教程”这个词——你不是在学语法,是在修一台解析器。它不报错就谢天谢地,一 panic 就得倒查四层上下文。
parser.ParseFile 调用前必须检查这四件事
90% 的崩溃不是代码写错,而是 parser.ParseFile 没配齐上下文:
-
fset必须是非 nil:用token.NewFileSet()创建,漏了会导致所有node.Pos()返回 0,后续定位失效 - 第二个参数(文件名)可传
"",但若用于磁盘读取,建议传绝对路径;若用于字符串解析(src非 nil),它只是占位符,不校验是否存在 - 第四个参数
mode不能为 nil:想拿注释就得加parser.ParseComments;想容忍语法错误继续解析,加parser.AllErrors -
src参数决定读取方式:传nil表示从文件路径读;传字符串(如os.ReadFile结果)则必须非 nil,否则解析器直接忽略
典型 panic:panic: interface conversion: ast.Node is nil, not *ast.File,基本等于你传了空字符串、非法 UTF-8,或没检查 err != nil 就直接访问 f。
ast.Inspect 回调里怎么安全取函数名和参数
别信文档说 “Params 是 *ast.FieldList” 就直接 .List——它真可能是 nil:
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 函数名在
f.Name.Name,不是f.Name;后者是*ast.Ident节点,未判空就取会 panic -
f.Type.Params和f.Type.Results都可能为 nil(比如func hello() {}或func() error),必须先判空再访问.List - 每个参数是
*ast.Field,名字在field.Names([]*ast.Ident),匿名参数(如func(int))该切片为空,取[0]必 panic -
f.Doc是函数上方的注释,但仅当mode含parser.ParseComments才非 nil;f.Comments是整个文件的注释列表,不是函数专属
正确写法:
if f, ok := node.(*ast.FuncDecl); ok && f.Name != nil && f.Name.Name == "main" {
if f.Type.Params != nil {
for _, field := range f.Type.Params.List {
// ...
}
}
}
为什么别手写递归遍历 AST
手写 switch node.(type) 看似自由,实则极易漏节点:
- Go 1.18 加了
ast.TypeSpec.TypeParams,1.21 加了ast.FieldList新字段,你漏一个,整棵树就断一层 - 只遍历
FuncDecl.Type.Params,却忘了FuncType.Results也是*ast.FieldList;或忽略GenDecl里嵌套的FuncLit(匿名函数) -
ast.Inspect内部已按 Go 语法规范完整覆盖所有字段路径,自动跳过 nil 子节点,且保证深度优先顺序稳定 - 它不会因嵌套过深导致栈溢出(标准库做了迭代优化)
但注意:ast.Inspect 不提供父节点或作用域信息——需要自己维护栈,或换用 ast.Walk + 自定义 Visitor。
大型项目多次遍历 AST 时该用 inspector
如果你要对同一组 AST 做多个分析(比如先找未使用变量,再统计函数复杂度,再提取注释),ast.Inspect 就成了性能瓶颈:
-
inspector.New([]*ast.File{f1, f2})初始化时一次性构建事件列表,后续遍历直接扫描索引,实测快 2.5 倍 - 只查函数?传
[]ast.Node{(*ast.FuncDecl)(nil)}给Preorder,避免无谓节点访问 - 需要前后序逻辑(比如进函数时计数、出函数时汇总),用
Nodes方法,它比Preorder多一层控制粒度 -
inspector不兼容老版本 Go 工具链,Go 1.21+ 更稳;低于此版本建议 stick withast.Inspect
最常被忽略的是:inspector 不自动进入 ast.CommentGroup,需显式调用 inspector.WithStack() 并检查 ast.CommentGroup 类型才能拿到注释内容。

















