美文网首页
使用clang获取代码中的全局变量、函数(名称,参数,返回值)、

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

作者: methon | 来源:发表于2021-05-12 19:01 被阅读0次

    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。大家有疑问随时联系。一定知无不言。

    相关文章

      网友评论

          本文标题:使用clang获取代码中的全局变量、函数(名称,参数,返回值)、

          本文链接:https://www.haomeiwen.com/subject/euexjltx.html