ast.Inspect可遍历*ast.File全部节点,但需配齐token.FileSet、parser.Mode(含ParseComments)、非空src及err检查;函数声明需判空f.Type.Params等字段;跨包类型信息必须用go/types。

直接用 ast.Inspect 遍历 *ast.File 就能拿到函数、结构体、接口等顶层声明,但必须配齐 token.FileSet、设对 parser.Mode、严格检查 err,否则一跑就 panic 或漏节点。
parser.ParseFile 调用前必须配齐四样东西
几乎所有崩溃都卡在这一步:传了 nil 的 fset、忘了加 parser.ParseComments、src 是空字节切片、或没检查 err != nil 就直接访问 file。
-
fset必须是token.NewFileSet()创建的非 nil 值,否则所有node.Pos()返回 0,定位失效 - 文件名参数可传空字符串
"",但若走磁盘读取(src == nil),建议传绝对路径;若走内存解析(src != nil),它只是占位符,不校验存在性 -
mode至少含parser.ParseComments(否则f.Doc为空)和/或parser.AllErrors(容忍语法错误继续解析) -
src决定读取方式:传nil表示从文件路径加载;传[]byte(如os.ReadFile结果)则必须非空,否则解析器跳过
遍历函数声明时为什么拿不到 func main 或参数名
*ast.FuncDecl 字段命名和语义脱节,直接取值极易 panic。比如 f.Name 是 *ast.Ident 节点,不是字符串;f.Type.Params 可能为 nil,不是空切片。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 函数名在
f.Name.Name,不是f.Name—— 后者未判空就取会崩 -
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
为什么优先用 ast.Inspect 而不是手写递归遍历 Decls
手写递归只扫 f.Decls,会漏掉大量真实存在的节点:闭包 func() {} 是 *ast.FuncLit,不在 Decls 里;结构体字段定义在 *ast.StructType.Fields,不经过 *ast.GenDecl 根本碰不到;switch 分支里的 fallthrough 在 *ast.CaseClause.Body 中。
立即学习“go语言免费学习笔记(深入)”;
-
ast.Inspect自动覆盖全部节点类型,包括*ast.FuncLit、*ast.CompositeLit、*ast.TypeSpec等冷门但高频结构 - 回调签名是
func(ast.Node) bool,返回false可立即中断(如找到第一个main就停) - 它跳过所有 nil 字段,避免手写时因
f.Type.Params == nil而 panic - 别在回调里改节点 —— AST 是只读结构,改了无效,还可能干扰后续遍历
跨包类型信息必须用 go/types,不能靠 ast 单干
go/ast 只管“长什么样”,不管“是什么类型”。比如 var x MyStruct,AST 里只有名字 x 和 MyStruct 字符串,没有字段列表、没有是否实现了某个接口的信息。
- 要查
MyStruct定义在哪、有哪些字段、是否嵌套其他结构体,必须用go/types加载整个包,构建类型图谱 -
go/types自动解析 import 路径、链接跨包对象、推导表达式类型,提供types.Info承载语义映射 - 不要自己实现
ast.Importer——go/types.Config默认用golang.org/x/tools/go/types/internal/typeutil.Importer,开箱即用 - 单靠
ast.Inspect拿不到跨文件的结构体成员或方法签名,这是最常被忽略的边界

















