You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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;
}

关键步骤说明

  1. 节点过滤:只处理函数调用类型的节点,其他节点直接递归跳过
  2. 目标函数匹配:通过clang_getCursorSpelling获取调用的函数名,和foo做字符串对比
  3. 参数处理:
    • 用clang_CallExpr_getNumArgs获取参数总数,逐个遍历
    • 用clang_getCursorType拿到参数类型,普通类型直接输出拼写
    • 枚举类型需要额外获取声明节点,再拿到枚举的具体名称
  4. 资源管理:所有CXString类型都要调用clang_disposeString释放,避免内存泄漏

运行输出结果

编译并运行这段代码后,你会得到如下输出:

Found call to foo with 2 arguments:
Argument 1: int
Argument 2: enum test

完美匹配你想要的结果,能准确识别出第二个参数是enum test类型~

备注:内容来源于stack exchange,提问作者shaharhoch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 12:49:30