如何通过Go语言AST判断CallExpr调用的是自定义还是内置函数
判断Go AST中
len调用是自定义函数还是内置函数 先看示例代码:
package main import "fmt" func len(s []int) string { return "xyz" } func main() { s := []int{1, 2, 3} fmt.Println(len(s)) }
这段代码里自定义的len([]int)会覆盖内置len,输出xyz。现在已知len(s)对应的ast.CallExpr节点,怎么通过Go的AST+类型检查判断调用的是自定义还是内置函数?
核心思路:语法AST不够,必须结合类型检查
单纯的AST语法分析只能看到一个名为len的标识符调用,没法区分是内置还是自定义——因为内置函数不会在AST里生成对应的函数定义节点,而自定义函数会有ast.FuncDecl。要确定实际调用的是哪个,必须用go/types包做类型检查,它会帮我们绑定标识符到实际的对象。
具体步骤
- 解析AST并做类型检查:用
parser.ParseFile解析代码得到AST,再用types.Config.Check进行类型检查,同时传入types.Info对象来记录每个标识符的绑定信息。 - 定位
ast.CallExpr的标识符:从ast.CallExpr的Fun字段取出对应的ast.Ident(也就是调用的函数名len)。 - 通过类型信息判断对象类型:
- 如果标识符绑定的是
*types.Builtin类型,说明调用的是内置函数; - 如果是
*types.Func类型,说明调用的是自定义函数(同名覆盖场景下即为用户自定义的函数)。
- 如果标识符绑定的是
示例实现代码
package main import ( "go/ast" "go/parser" "go/token" "go/types" "log" ) func main() { fset := token.NewFileSet() src := `package main import "fmt" func len(s []int) string { return "xyz" } func main() { s := []int{1, 2, 3} fmt.Println(len(s)) }` // 解析代码生成AST file, err := parser.ParseFile(fset, "main.go", src, parser.AllErrors) if err != nil { log.Fatalf("解析失败: %v", err) } // 初始化类型检查配置 conf := types.Config{} info := &types.Info{ Uses: make(map[*ast.Ident]types.Object), // 记录标识符的使用信息 } // 执行类型检查 if _, err := conf.Check("main", fset, []*ast.File{file}, info); err != nil { log.Fatalf("类型检查失败: %v", err) } // 遍历AST找到目标CallExpr ast.Inspect(file, func(n ast.Node) bool { callExpr, ok := n.(*ast.CallExpr) if !ok { return true } // 确认调用的函数是标识符len funcIdent, ok := callExpr.Fun.(*ast.Ident) if !ok || funcIdent.Name != "len" { return true } // 获取标识符绑定的对象 if obj, exists := info.Uses[funcIdent]; exists { switch obj.(type) { case *types.Builtin: log.Println("当前调用的是内置len函数") case *types.Func: log.Println("当前调用的是自定义len函数") default: log.Println("未知的函数类型") } } return false // 找到目标后停止遍历 }) }
这段代码运行后会输出当前调用的是自定义len函数,因为类型检查器识别到当前作用域中存在自定义的len函数,优先级高于内置函数。
内容的提问来源于stack exchange,提问作者Rasel
相关产品推荐
相关产品推荐

