ast.Inspect 拿不到变量实际类型,因 ast 包仅处理语法树,不进行类型推导;需联合 go/types 包,通过 types.Info.Types 映射 AST 表达式节点到具体类型。

为什么 ast.Inspect 拿不到变量的实际类型
因为 Go 的 ast 包只处理语法树,不涉及类型检查——它看到的是 var x int 里的 int 字面量,但无法知道 x 在泛型函数中被推导为 string,也无法识别 var y = 42 实际是 int 还是 int64(取决于上下文)。真正类型信息必须靠 types.Info 配合 go/types 包。
用 go/types + ast 联合获取变量类型
核心流程是:先用 parser.ParseFiles 构建 AST,再用 types.NewChecker 对同一文件做类型检查,最后通过 types.Info.Types 映射回 AST 节点。
-
types.Info.Types是map[ast.Expr]types.TypeAndValue,键是 AST 表达式节点(比如*ast.Ident),值含Type字段 - 对变量声明语句(
*ast.AssignStmt或*ast.GenDecl),需遍历其Lhs或中的 <code>*ast.Ident,再查types.Info.Types - 注意:只有已定义且可导出的标识符才在
Types中有记录;未使用的局部变量可能缺失
// 示例:获取 var x = "hello" 中 x 的类型
ident := node.(*ast.Ident)
if tv, ok := info.Types[ident]; ok {
fmt.Printf("%s → %s\n", ident.Name, tv.Type.String()) // → string
}
ast.TypeSpec 和 ast.AssignStmt 的处理差异
显式类型声明(var x int)和隐式推导(var x = 1)在 AST 中结构不同,但类型信息来源一致——都依赖 types.Info.Types,而非 AST 自身。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
*ast.TypeSpec(如type MyInt int)的Name在types.Info.Defs中,对应*types.TypeName,其Type()返回底层类型 -
*ast.AssignStmt的左值Lhs是[]ast.Expr,需逐个断言为*ast.Ident后查info.Types -
*ast.GenDecl(var/const/type块)需遍历Specs,对*ast.ValueSpec取Names并查类型
常见坑:类型字符串不直观、泛型和接口处理难
tv.Type.String() 输出如 main.T 或 func(int) string,但不会自动展开别名或接口方法集;更麻烦的是泛型实例化后的类型(如 slice[int])在旧版 Go 中可能显示为 []int,丢失泛型信息。
立即学习“go语言免费学习笔记(深入)”;
- 用
types.TypeString(tv.Type, nil)可控制格式,但无法还原源码写法(比如type T = []int总显示为[]int) - 接口类型需调用
tv.Type.Underlying().(*types.Interface).ExplicitMethod()才能拿到方法,直接String()只输出interface{...} - 泛型参数类型要检查
tv.Type是否为*types.Named,再调用Origin()和TypeArgs()获取实参
类型信息不是“读出来就完事”,它依赖完整的包加载和类型推导上下文——漏掉 imports 或没设置 Config.IgnoreImports = false,types.Info 就会大量为空。

















