使用clang获取代码中的全局变量、函数(名称,参数,返回值)、结构体信息

1.下载clang源码

本人使用的为clang10.0,visual studio版本为2017(亲测2019会有问题)
clang源码下载地址为 https://releases.llvm.org/download.html#10.0.0

image.png

2.编写clang插件

1.解压刚才的源码,进入到llvm\tools\clang\tools\extra目录下,创建文件夹,为自己的项目名称。


image.png

2.进入到自己项目名称的文件夹,将自己写好的c文件和h文件放到此文件夹中。新建文件CMakeLists.txt


image.png

3.编写CMakeLists.txt,规则见图
image.png

4.返回上一级目录,即extra这一层,打开此层的CMakeLists.txt,结尾加入:


image.png

3.下载cmake

1.cmake下载地址https://cmake.org/download/
2.下载64位版本

image.png

3.傻瓜安装,记得加入到环境变量中
image.png

4.使用cmake进行编译(cmake编译依赖python解释器,记得安装python)

1.打开cmake,选择clang的源码目录和编译目录


image.png

2.选择visual stdio版本(亲测clang10.0 visual stdio2019 无法编译通过,请使用visual stdio2017)


image.png

3.点击配制,点击生成
image.png

4.生成完成后,即可在编译目录看见相应的vc工程(双击LLVM.sln。打开工程)


image.png

5.在Clang executables下可以发现自己的工程
image.png

4.ASTMatcher

1.ASTMatcher类似于正则表达式,书写规则参考官网。很详细https://clang.llvm.org/docs/LibASTMatchersReference.html
2.我们即使用ASTMatcher来匹配所有的全局变量、函数(名称,参数,返回值)、结构体信息

image.png

如varDecl为变量,hasGlobalStorage可以筛选全局变量。

5.处理全局变量

1.获取到了全局变量节点,接下来主要调用clang的接口获取全局变量的名称,类型,维度,初值等信息存储到我们自定义的数据结构中。

VariableInfoStruct variableInfoStruct;
    /* 获取变量名 */
    std::string nameStr = varDeclNode->getNameAsString();
    std::string initValueStr = "";
    std::string fileName = getNodePath(varDeclNode->getLocation(), Result);
    auto firstDeclNode = varDeclNode->getFirstDecl();
    std::string firstDeclPath = getNodePath(firstDeclNode->getLocation(), Result);
    variableInfoStruct.hPath =
        strcpy(new char[firstDeclPath.length() + 1], firstDeclPath.data());
    variableInfoStruct.path =
        strcpy(new char[fileName.length() + 1], fileName.data());
    variableInfoStruct.name =
        strcpy(new char[nameStr.length() + 1], nameStr.data());
    variableInfoStruct.nodeLocInfo = getLineNumInfo(varDeclNode->getSourceRange(), Result.Context);
    /* 获取变量类型,不带类型信息 */
    std::string typeStr = varDeclNode->getType()
        .getLocalUnqualifiedType()
        .getCanonicalType()
        .getAsString();
    QualType nodeType = varDeclNode->getType();
    // std::cout << varDeclNode->getType().getTypePtr()->getTypeClassName();
    const Type *type = varDeclNode->getType().getTypePtr();
    std::string name = clang::QualType(type, 0).getAsString();
    // std::cout << nodeType.getCanonicalType().getAsString() << "\n";
    dealType(typeStr, nodeType, &variableInfoStruct);
    /* 获取初始化器 */
    const Expr *initExpr = varDeclNode->getAnyInitializer();
    /* 处理全局变量的初值 */
    std::vector<InitAstStruct> initAstStructVector;
    dealInitValue(initExpr, &initValueStr, Result, &initAstStructVector);
    variableInfoStruct.initValue =
        strcpy(new char[initValueStr.length() + 1], initValueStr.data());
    variableInfoStruct.initAstStruct = initAstStructVector;
    cfile->globalVars.push_back(variableInfoStruct);

6.函数

1.获取到了函数节点,接下来主要调用clang的接口获取函数的名称,参数类型,参数名称,返回值名称,返回值类型等信息存储到我们自定义的数据结构中。

 const FunctionDecl *firstDeclNode = functionDeclNode->getFirstDecl();
    std::string firstDeclFilePath = getNodePath(firstDeclNode->getLocation(), Result);
    FunctionInfoStruct functionInfoStruct;
    std::string fileName = getNodePath(functionDeclNode->getLocation(), Result);
    functionInfoStruct.path = strcpy(new char[fileName.length() + 1], fileName.data());
    functionInfoStruct.hPath = strcpy(new char[firstDeclFilePath.length() + 1], firstDeclFilePath.data());
    std::string nameStr = functionDeclNode->getNameAsString();
    std::string typeStr = functionDeclNode->getReturnType().getAsString();
    functionInfoStruct.name = strcpy(new char[nameStr.length() + 1], nameStr.data());
    functionInfoStruct.type = strcpy(new char[typeStr.length() + 1], typeStr.data());
    functionInfoStruct.nodeLocInfo = getLineNumInfo(functionDeclNode->getSourceRange(), Result.Context);
    functionInfoStruct.paraCount = functionDeclNode->getNumParams();

    unsigned int paraNum = functionDeclNode->getNumParams();
    if (paraNum > 0) 
    {
        for (int i = 0; i < paraNum; i++)
        {
            VariableInfoStruct variableInfoStruct;
            std::string nameStr = functionDeclNode->getParamDecl(i)->getNameAsString();
            variableInfoStruct.name = strcpy(new char[nameStr.length() + 1], nameStr.data());

            /* 获取函数参数的类型 */
            const clang::ParmVarDecl *paramDecl = functionDeclNode->getParamDecl(i);
            QualType parType = paramDecl->getType();
            QualType parLocalUnqualifiedType = parType.getLocalUnqualifiedType();
            QualType parCanonicalType = parLocalUnqualifiedType.getCanonicalType();
            std::string typeStr = parCanonicalType.getAsString();

            /* 将函数参数的类型转换为Type,方便后续判断是否为数组类型 */
            const Type *type = functionDeclNode->getParamDecl(i)->getType().getTypePtr();
            clang::SourceRange srcRange = functionDeclNode->getParamDecl(i)->getSourceRange();
            variableInfoStruct.nodeLocInfo = getLineNumInfo(srcRange, Result.Context);

            /* 说明是Decayed类型,即本来是数组类型,被clang转换成指针类型 */
            QualType nodeType = functionDeclNode->getParamDecl(i)->getType();
            if (type->getTypeClass() == 8) {
                const DecayedType *DT = type->getAs<DecayedType>();
                type = DT->getOriginalType().getTypePtr();
                nodeType = DT->getOriginalType();
            }
            dealType(typeStr, nodeType, &variableInfoStruct);
            functionInfoStruct.paras.push_back(variableInfoStruct);
        }
    }
    cfile->functions.push_back(functionInfoStruct);

7.结构体

1.获取到了结构体节点,接下来主要调用clang的接口获取结构体的名称,成员变量等信息存储到我们自定义的数据结构中。

if (varRecordDeclNode->field_begin() != varRecordDeclNode->field_end())
    {
        /* 说明结构体有成员 */
        StructInfo structInfo;
        structInfo.nodeLocInfo = getLineNumInfo(varRecordDeclNode->getSourceRange(), Result.Context);
        std::string nameStr = "";
        std::string qualifier = "";

        /* 如果是匿名结构体,则获取匿名结构体的名称 */
        std::string anonymousName = getAnonymousName(varRecordDeclNode, Result);
        if (!anonymousName.empty()) {
            nameStr = anonymousName;
        }
        if (nameStr.empty() && varRecordDeclNode->getTypedefNameForAnonDecl()) {
            /* 获取结构体的typedef名称 */
            nameStr = varRecordDeclNode->getTypedefNameForAnonDecl()->getNameAsString();
        }
        if (nameStr.empty()) {
            /* 直接获取结构体的名字 */
            nameStr = varRecordDeclNode->getNameAsString();
        }
        else {
            qualifier = "typedef";
        }

        std::string structType = "";
        if (varRecordDeclNode->isStruct()) {
            structType = "struct";
        }
        else {
            structType = "union";
        }

        structInfo.type = strcpy(new char[structType.length() + 1], structType.data());
        std::string fileName = getNodePath(varRecordDeclNode->getLocation(), Result);
        structInfo.path = strcpy(new char[fileName.length() + 1], fileName.data());
        structInfo.name = strcpy(new char[nameStr.length() + 1], nameStr.data());
        structInfo.qualifier = strcpy(new char[qualifier.length() + 1], qualifier.data());

        /* 记录结构体的field数量 */
        for (RecordDecl::field_iterator jt = varRecordDeclNode->field_begin(); jt != varRecordDeclNode->field_end(); jt++)
        {
            dealRecordField(&structInfo, jt, Result);
        }

        cfile->structs.push_back(structInfo);

        return (structIdx + 1);
    }
    else
    {
        return structIdx;
    }

本项目完整的代码见https://github.com/BondChang/GetCFileInfo.git。大家有疑问随时联系。一定知无不言。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容