直接调用parser.ParseFile获取ast.File根节点,需传入token.NewFileSet()、合法filename(或"")、src(文件路径或字符串)及mode(如ParseComments);遍历用ast.Inspect并类型断言ast.FuncDecl,注意判空Params/Results等易panic字段。

怎么用 go/ast 解析一个 Go 文件得到语法树
直接调用 parser.ParseFile 是最常用入口,它返回 *ast.File,也就是整棵树的根节点。注意必须传入有效的 token.FileSet,否则后续位置信息(如行号)全为空或 panic。
常见错误是忽略 token.FileSet 初始化,导致 ast.Print 输出时显示 Pos=0、End=0,或者 ast.Inspect 遍历时无法定位节点位置。
- 先创建
token.NewFileSet(),这是所有位置信息的底层支撑 -
parser.ParseFile(fset, filename, src, parser.ParseComments)—— 第四个参数控制是否保留注释,不加的话ast.CommentGroup全丢掉 - 如果从字符串解析(非文件),
src传入字符串内容,filename可设为"<input>",但fset仍不可省
如何遍历语法树并识别函数定义
ast.Inspect 是最轻量通用的遍历方式,它按深度优先顺序访问每个节点,并允许你在回调中根据类型做判断。别用 ast.Walk(需要自己实现 Visitor 接口),除非你明确要中断遍历或复用 visitor 实例。
识别函数定义的关键是判断节点是否为 *ast.FuncDecl,但要注意:方法(receiver)和普通函数都走这个类型,区别在 Fn.Recv 字段是否为 nil。
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 用
if fn, ok := node.(*ast.FuncDecl); ok { ... }类型断言捕获函数声明 -
fn.Name.Name是函数名;fn.Recv非 nil 表示是方法;fn.Type.Params和fn.Type.Results分别对应参数与返回值列表 - 别直接读
fn.Body.List拿语句 —— 它是[]ast.Stmt,每条语句还需进一步类型断言(比如*ast.ExprStmt、*ast.ReturnStmt)
为什么 ast.Inspect 修改节点不生效
ast.Inspect 的回调函数接收的是节点指针的副本(即 node interface{} 底层是 **ast.XXX),但你不能靠赋值修改父节点的子字段 —— Go 的 interface 值传递不会穿透到上层结构体字段。
真正能改树的方式只有两种:一是在 parser.ParseFile 后手动构造新节点并替换(适合小范围修改);二是用 astutil.Apply(来自 golang.org/x/tools/go/ast/astutil),它提供可中断的、带父节点上下文的重写逻辑。
- 直接写
node = &ast.BadExpr{}完全无效,只是改了局部变量 - 想删某个
ast.Expr,得找到它所在的父节点(如*ast.CallExpr),再修改其Fun或Args字段 -
astutil.Apply的pre函数可返回skipChildren控制是否继续进子树,apply函数返回替换后的节点
如何安全提取变量名和字面量值
提取标识符(如变量名、函数名)用 node.(*ast.Ident).Name 即可,但字面量(123、"hello"、true)类型分散,必须按具体子类型处理:*ast.BasicLit(基础字面量)、*ast.CompositeLit(复合字面量)、*ast.FuncLit(匿名函数)等。
特别注意 *ast.BasicLit 的 Kind 字段决定内容解读方式:token.INT 对应整数(Value 是字符串如 "42",需用 strconv.ParseInt 转);token.STRING 的 Value 是带引号的原始字符串(如 "\"foo\""),要用 strconv.Unquote 才能得到 "foo"。
- 别对
*ast.BasicLit直接用fmt.Sprint,那输出的是 Go 源码形式,不是运行时值 - 数组、结构体字面量得递归进
Elts或Fields字段,它们仍是ast.Expr,需再次断言 - 未初始化的变量声明(如
var x int)对应*ast.ValueSpec,其Values字段为 nil,别贸然取[0]
ast 树里没有类型信息(如 int 是什么底层类型)、没有作用域分析、也不展开 import 别名 —— 它只忠实反映源码文本结构。真要查变量类型或跨文件引用,得配合 go/types 或 golang.org/x/tools/go/packages 做类型检查,那是另一层工作。

















