直接用ast.inspect配合类型断言可做绝大多数静态分析,但必须防nil、带fset、设对mode,否则易panic或结果为空;parser.parsefile前须检查fset非nil、文件名合理、mode含parsecomments或allerrors、src非nil且err已校验。

直接用 ast.Inspect 配合类型断言就能做绝大多数静态分析,不用手写递归、不需构造节点、也不依赖 go/types —— 但必须防 nil、带 fset、设对 mode,否则一跑就 panic 或结果为空。
parser.ParseFile 调用前必须检查这四件事
几乎所有崩溃都源于调用前上下文没配齐:
-
fset必须是token.NewFileSet()创建的非 nil 值,否则所有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.FuncDecl,基本等于你没检查 err != nil 就直接访问了 file。
遍历函数声明时为什么找不到 main 或参数名
*ast.FuncDecl 字段命名和语义脱节,直接取容易 panic:
- 函数名在
f.Name.Name,不是f.Name—— 后者是*ast.Ident节点,未判空就取会崩 -
f.Type.Params和f.Type.Results可能为nil(比如func hello() {}),不是空切片,必须先判空再访问.List - 参数是
*ast.Field,名字在field.Names([]*ast.Ident),匿名参数(如func(int))该切片为空,取[0]必 panic -
f.Doc是函数上方的注释,但仅当mode包含parser.ParseComments才非 nil;f.Comments是整个文件注释列表,不是函数专属
ast.Inspect 回调里怎么安全提取信息
它比手写递归稳,但用法有硬约束:
- 回调签名是
func(node ast.Node) bool,返回false表示“别往下走了”,适合找第一个main就停 - 类型断言必须带
ok判断:if f, ok := node.(*ast.FuncDecl); ok { ... },否则遇到*ast.FuncLit(闭包)会静默跳过 - 别在回调里改
node字段 —— AST 是只读结构,改了无效,还可能干扰后续遍历 - 需要父节点或作用域信息?
ast.Inspect不提供,得自己维护栈;真要改 AST,得用golang.org/x/tools/go/ast/astutil - 避免在回调里做 heavy I/O 或正则匹配,会拖慢整棵树遍历
为什么 FuncDecl.Type.Params 有时是 nil
Go 允许无参函数,Params 字段就是 nil,不是 &ast.FieldList{List: []*ast.Field{}}:
正确写法永远先判空:
if f.Type.Params != nil {
for _, field := range f.Type.Params.List {
// 处理每个参数
}
}
同理,Results 也可能为 nil;省略号参数(如 func(...int))对应 *ast.Ellipsis 类型,不是普通 *ast.ArrayType。内嵌字段(如 struct{ A, B int })在 FieldList 中表现为单个 *ast.Field,但 Names 是长度为 2 的切片 —— 这些细节不查源码很容易漏。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











