clang libtool 实现 macro 解析

国内写clang的libtool的文章较少,打算写一些有趣但无关紧要的clang api介绍。

预处理回调类:

class PreprocessorCallbacks:public clang::PPCallbacks{
  void MacroDefined(const Token &macroNameToken, const MacroDirective *MD)//源码解析到 #define回调
  void MacroExpands(...)//源码发生宏展开时调用。
}

如何获取某个宏的定义呢?通过重写MacroDefined方法,macroNameToken.getIdentifierInfo()->getNameStart()
如果获取function liked宏的参数呢?macroArgs->getUnexpArgument(0).getIdentifierInfo()->getNameStart()
但这样获取的参数有时无法print,例如get_user(target, (uint32_t __user )ptr)),这时我们需要获取tokens, ((clang::MacroArgs)macroArgs)->getPreExpArgument(1, preprocessor);再写一个while循环来解析。

这个解析非常难写,可用性不高,更通用的办法是:

  1. 获取宏定义在文件中的位置
  2. 根据位置匹配StmtExpr,找出所有引用
  3. 手工编写match匹配需要的内容
class PreprocessorCallbacks:public clang::PPCallbacks{
public:
    explicit PreprocessorCallbacks(clang::SourceManager& sourceManager, Preprocessor & preprocessor):
        sourceManager(sourceManager), preprocessor(preprocessor){
    }

    void MacroDefined(const Token &macroNameToken, const MacroDirective *MD) override {

        if(macroNameToken.getIdentifierInfo()->getNameStart()==std::string("get_user")){
            const MacroInfo* mi = MD->getMacroInfo();
            MD->dump();
        }
    }

     void MacroExpands(
            const clang::Token& macroNameToken,
            const clang::MacroDefinition& macroDirective,
            clang::SourceRange range,
            const clang::MacroArgs* macroArgs) override  {

            //#define n
        MacroInfo* macroInfo=macroDirective.getMacroInfo();
        UserCopyMacroLocAndArg macro;

        if(macroInfo && macroArgs &&
            macroNameToken.getIdentifierInfo()->getNameStart()==std::string("get_user")){
            //macroInfo->getDefinitionLoc().dump(sourceManager);
            //macroInfo->dump();

            const Token * to_token=macroArgs->getUnexpArgument(0);
            const Token * from_token=nullptr;
            std::vector<Token> tokens = ((clang::MacroArgs*)macroArgs)->getPreExpArgument(1, preprocessor);
            int idx=0;
            Token type_token;
            std::string from_name;
            while(idx < tokens.size()){
                if(std::string("l_paren") == tokens[idx].getName() &&
                    std::string("star") == tokens[idx+2].getName()){
                    type_token = tokens[idx+1];
                    idx+=3;
                }
                else if(std::string("r_paren") == tokens[idx].getName()){
                    from_token=&tokens[idx+1];
                    break;
                }
            }

            assert(from_token);

            macro.loc = macroInfo->tokens_begin()->getLocation().printToString(sourceManager);
            macro.name = macroNameToken.getIdentifierInfo()->getNameStart();
            macro.to = std::string(to_token->getIdentifierInfo()->getNameStart());
            macro.from = std::string(from_token->getIdentifierInfo()->getNameStart());
            macro.type = std::string(type_token.getIdentifierInfo()->getNameStart());
            macroData.push_back(macro);

            /*
            if(macroInfo->getNumTokens()>0){
                for(auto arg : macroInfo->tokens()){
                    std::string loc(arg.getLocation().printToString(sourceManager));
                    //std::cout << arg.getName() << ": " << loc << "-- ";
                    //token contain all define value,
                    if(arg.getIdentifierInfo()){
                        //std::cout << arg.getIdentifierInfo()->getNameStart() << std::endl;
                        //arg.getLocation().dump(sourceManager);
                    }
                }
                //std::cout << std::endl;
            }
             */
        }
        else{
            return;
        }
        std::cout << "macro.to:\t" << macro.to << std::endl;
        std::cout << "macro.from:\t" << macro.from << std::endl;
            //macroNameToken.getLocation().dump(sourceManager);
    }

private:
    clang::SourceManager& sourceManager;
    Preprocessor & preprocessor;
};