如何使用golang.org/x/tools/analysis获取实例化泛型类型
问题:使用golang.org/x/tools/analysis获取实例化泛型类型时找不到对应实例
尝试通过pass.TypesInfo.Instances映射获取实例化的泛型类型,但用CallExpr.Fun.(*ast.Ident)作为键时,映射中无对应条目(已确认映射不为空)。
相关代码
调用逻辑
// v is *ast.CallExpr from a type switch callee := pass.TypesInfo.TypeOf(v.Fun) if f, ok := callee.(*types.Signature); ok { f = expandGenerics(pass, v.Fun, f) }
泛型展开函数
/// Super simple expander for generics func expandGenerics(pass *analysis.Pass, callee ast.Expr, s *types.Signature) *types.Signature { if s.TypeParams() == nil { return s } if callee, ok := callee.(*ast.Ident); ok { log.Printf("Instance: %s\n", callee.Name) instance, ok := pass.TypesInfo.Instances[callee] if ok { log.Printf("Generic: %s\n", TypeString(pass.Pkg, instance.Type)) if s, ok := instance.Type.(*types.Signature); ok { return s } } else { log.Printf("Unknown: %s\n", TypeString(pass.Pkg, s)) //for key, ty := range pass.TypesInfo.Instances { // log.Printf("Keys: %s (%s)\n", key, TypeString(pass.Pkg, ty.Type)) //} } } return s }
我也参考了官方源码,发现它使用的标识符逻辑和我的代码完全一致,对应代码片段如下:
// 获取泛型实例的逻辑 if ident, ok := expr.(*ast.Ident); ok { if inst, ok := pkg.GetTypesInfo().Instances[ident]; ok { // 处理实例化类型 targs := inst.TypeArgs if len(targs) > 0 { // 添加类型参数的内嵌提示 hints = append(hints, inlayHintForTypeArgs(pkg, ident, targs)) } } }
问题原因及解决方法
原因
当调用泛型函数(如Foo[int]())时,CallExpr.Fun对应的是*ast.IndexExpr而非直接的*ast.Ident。TypesInfo.Instances的键是实例化节点中指向原始泛型的标识符,也就是IndexExpr.X对应的*ast.Ident,而非CallExpr.Fun直接转换的Ident。
解决方法
修改代码,先解析CallExpr.Fun中可能存在的IndexExpr,提取正确的标识符作为键:
调整泛型展开函数
func expandGenerics(pass *analysis.Pass, callee ast.Expr, s *types.Signature) *types.Signature { if s.TypeParams() == nil { return s } // 解析泛型实例化的语法树节点 var targetIdent *ast.Ident switch expr := callee.(type) { case *ast.Ident: targetIdent = expr case *ast.IndexExpr: // 泛型调用的Fun是IndexExpr,提取其内部的原始泛型标识符 if idxIdent, ok := expr.X.(*ast.Ident); ok { targetIdent = idxIdent } } if targetIdent != nil { log.Printf("Instance: %s\n", targetIdent.Name) instance, ok := pass.TypesInfo.Instances[targetIdent] if ok { log.Printf("Generic: %s\n", TypeString(pass.Pkg, instance.Type)) if sig, ok := instance.Type.(*types.Signature); ok { return sig } } else { log.Printf("Unknown: %s\n", TypeString(pass.Pkg, s)) } } return s }
调整调用逻辑
// v is *ast.CallExpr from a type switch callee := pass.TypesInfo.TypeOf(v.Fun) if f, ok := callee.(*types.Signature); ok { // 直接传入CallExpr.Fun,让expandGenerics内部处理IndexExpr解析 f = expandGenerics(pass, v.Fun, f) }
额外说明
官方源码处理的是直接引用泛型实例(如var f = Foo[int])的场景,此时语法树中Expr是*ast.IndexExpr;而你的场景是调用泛型实例函数,CallExpr.Fun才是*ast.IndexExpr,二者语法树结构不同,但核心都是提取IndexExpr.X对应的标识符。
内容的提问来源于stack exchange,提问作者kdy
相关产品推荐
相关产品推荐

