用 ast.inspect 遍历需守卫节点层级,推荐直接遍历 f.decls:对每个 *ast.gendecl 若 tok==token.var,则遍历其 specs 中 *ast.valuespec 的 names 字段提取变量名。

用 ast.Inspect 遍历文件节点,过滤 *ast.GenDecl 中的 var 声明
Go 的 AST 不直接提供“全局变量列表”这个抽象概念,得自己从 *ast.File 里捞:所有顶层声明(*ast.GenDecl)中,Tok 是 token.VAR 的才是变量声明。注意不是所有 GenDecl 都是 var,还可能是 const 或 type。
实操建议:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用
parser.ParseFile解析源码得到*ast.File - 传给
ast.Inspect,在回调里判断节点是否为*ast.GenDecl且g.Tok == token.VAR - 每个
GenDecl的Specs是一个切片,里面每个元素是*ast.ValueSpec,对应一个或多个变量(比如var a, b int算一个ValueSpec) - 别漏掉带类型或初始化表达式的变量——它们的
Names字段就是标识符列表,Names[i].Name就是变量名
区分包级变量和函数内变量,只取 ast.File 直接子节点
ast.Inspect 会递归进所有嵌套节点,包括函数体里的 var。但函数内变量不是“全局”的,必须排除。
实操建议:
- 在
Inspect回调里加一层守卫:只处理node == f(即当前节点等于传入的*ast.File)或节点是*ast.GenDecl且父节点是*ast.File - 更稳妥的做法是不用
Inspect,改用手动遍历f.Decls—— 这个切片只含文件顶层声明,天然排除函数体内内容 - 注意:
f.Decls里可能有*ast.FuncDecl、*ast.GenDecl、*ast.TypeSpec等,务必先类型断言
处理导出/非导出变量、带初始化器的变量名提取
变量名是否导出(首字母大写)不影响提取逻辑,但名字本身要从 *ast.ValueSpec 的 Names 字段取,不是 Values 或 Type。
实操建议:
-
ValueSpec.Names是[]*ast.Ident,每个Ident.Name就是变量名字符串 - 如果某
ValueSpec没有Names(极少见,比如只有类型别名式声明),跳过 - 初始化表达式(
Values)和类型(Type)字段可选,不影响变量名存在性;但若想获取类型信息,需用types.Info配合types.Checker,纯 AST 拿不到具体类型 - 别把
ValueSpec.Type当变量名——那是类型字面量,比如var x struct{}里的struct{}
完整最小可运行示例(不依赖 go/types)
下面这段代码能正确列出 main.go 文件中所有包级 var 变量名:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func main() {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "main.go", nil, parser.ParseComments)
if err != nil {
panic(err)
}
var vars []string
for _, decl := range f.Decls {
gen, ok := decl.(*ast.GenDecl)
if !ok || gen.Tok != token.VAR {
continue
}
for _, spec := range gen.Specs {
valueSpec, ok := spec.(*ast.ValueSpec)
if !ok {
continue
}
for _, ident := range valueSpec.Names {
vars = append(vars, ident.Name)
}
}
}
fmt.Println(vars) // 输出类似 ["a", "b", "config"]
}
注意:这个逻辑不处理 var () 块外的单行 var x int——其实它也是 GenDecl,已覆盖。真正容易被忽略的是:AST 不包含作用域信息,所以无法靠它判断变量是否被 shadowed 或是否实际可达;如需语义分析,必须上 go/types。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










