使用libclang获取函数调用参数的枚举类型
使用libclang获取函数调用参数的枚举类型
嘿,我来帮你搞定用libclang识别特定函数调用并获取参数类型(包括枚举类型)的问题!你的代码已经有了不错的开头,我来帮你补全逻辑,让它能准确拿到foo函数调用的参数类型,尤其是枚举类型的信息。
核心思路
我们需要在遍历AST(抽象语法树)的过程中:
- 先定位到
CallExpr类型的节点(也就是函数调用节点) - 确认这个调用的目标函数是我们要找的
foo - 遍历每个调用参数,获取参数的类型,对枚举类型做特殊处理,拿到它的具体枚举名称
完整实现代码
我把你的visitFuncCalls函数补全并加上详细注释:
#include <clang-c/Index.h> #include <string.h> #include <stdio.h> static enum CXChildVisitResult visitFuncCalls(CXCursor current_cursor, CXCursor parent, CXClientData client_data) { // 跳过非函数调用的节点,继续递归遍历子节点 if (clang_getCursorKind(current_cursor) != CXCursor_CallExpr) { return CXChildVisit_Recurse; } static const char *TARGET_FUNC = "foo"; // 获取当前调用的函数名 CXString func_name = clang_getCursorSpelling(current_cursor); const char *func_name_cstr = clang_getCString(func_name); // 如果不是目标函数,释放资源后继续递归 if (strcmp(func_name_cstr, TARGET_FUNC) != 0) { clang_disposeString(func_name); return CXChildVisit_Recurse; } clang_disposeString(func_name); // 获取函数调用的参数数量 unsigned int arg_count = clang_CallExpr_getNumArgs(current_cursor); printf("Found call to %s with %u arguments:\n", TARGET_FUNC, arg_count); // 逐个遍历参数 for (unsigned int i = 0; i < arg_count; i++) { // 获取参数对应的cursor CXCursor arg_cursor = clang_CallExpr_getArg(current_cursor, i); // 获取参数的类型 CXType arg_type = clang_getCursorType(arg_cursor); // 获取类型的拼写(比如int) CXString type_spelling = clang_getTypeSpelling(arg_type); const char *type_cstr = clang_getCString(type_spelling); printf("Argument %u: ", i + 1); // 判断当前参数类型是否为枚举 if (arg_type.kind == CXType_Enum) { // 获取枚举类型的声明节点,进而拿到枚举名称 CXCursor enum_decl = clang_getTypeDeclaration(arg_type); CXString enum_name = clang_getCursorSpelling(enum_decl); printf("enum %s", clang_getCString(enum_name)); clang_disposeString(enum_name); } else { printf("%s", type_cstr); } printf("\n"); clang_disposeString(type_spelling); } // 继续递归遍历其他节点,避免漏掉其他foo的调用 return CXChildVisit_Recurse; } // 辅助函数:初始化libclang并解析目标代码 void parseTargetCode(const char *code) { CXIndex index = clang_createIndex(0, 0); // 解析内存中的代码,也可以替换成本地文件路径 CXTranslationUnit tu = clang_parseTranslationUnit( index, NULL, (const char*[]){"-x", "c", "-std=c99"}, 4, (const unsigned char*)code, strlen(code), CXTranslationUnit_None ); if (tu == NULL) { fprintf(stderr, "Failed to parse translation unit\n"); clang_disposeIndex(index); return; } // 从翻译单元的根节点开始遍历AST CXCursor root_cursor = clang_getTranslationUnitCursor(tu); clang_visitChildren(root_cursor, visitFuncCalls, NULL); // 释放资源,避免内存泄漏 clang_disposeTranslationUnit(tu); clang_disposeIndex(index); } // 测试你的示例代码 int main() { const char *test_code = R"( void foo(int a, ...) {} enum test { ENUM_VAL1, ENUM_VAL2 }; int main() { enum test e = ENUM_VAL1; int a = 1; foo(a, e); } )"; parseTargetCode(test_code); return 0; }
关键步骤说明
- 节点过滤:只处理函数调用类型的节点,其他节点直接递归跳过
- 目标函数匹配:通过
clang_getCursorSpelling获取调用的函数名,和foo做字符串对比 - 参数处理:
- 用
clang_CallExpr_getNumArgs获取参数总数,逐个遍历 - 用
clang_getCursorType拿到参数类型,普通类型直接输出拼写 - 枚举类型需要额外获取声明节点,再拿到枚举的具体名称
- 用
- 资源管理:所有
CXString类型都要调用clang_disposeString释放,避免内存泄漏
运行输出结果
编译并运行这段代码后,你会得到如下输出:
Found call to foo with 2 arguments: Argument 1: int Argument 2: enum test
完美匹配你想要的结果,能准确识别出第二个参数是enum test类型~
备注:内容来源于stack exchange,提问作者shaharhoch
相关产品推荐
相关产品推荐

