1

我正在编写某种工具来提取 C++ 代码的接口定义。在编写过程中,我决定将解析器限制为仅处理显式标记为要处理的代码,并且我认为 C++ 属性是最好的方法。我更愿意为[[export]]我想要导出的实体添加例如注释,但我意识到如果没有在 Clang 代码本身中注册自定义属性(我的意思是将属性添加到tools/clang/include/clang/Basic/Attr.td),libTooling 无法查看自定义属性。

因此,我的问题是:有没有办法在不修改该文件的情况下注册属性(例如通过以编程方式注册属性或编写自己的Attr.td文件)?

UPD:我正在使用 ASTMatchers 库进行源代码分析,因此基于访问者的方法可能不适合我。

4

1 回答 1

1

据我所知,如果不直接修改 libtooling,就无法注册自定义属性。

如果您愿意使用预处理器宏而不是属性,则可以使用我过去做过的解决方法。基础是我们将声明一个空宏,编写一个预处理器回调来识别宏的位置并将其存储在队列中,然后在 AST 访问器中,我们将访问类、方法或变量的记录,并检查前面的实体是否是我们的宏。

对于预处理器,您需要扩展clang::PPCallbacks和实现该MacroExpands方法。

void MyPreProcessorCallback::MacroExpands(const clang::Token& MacroNameTok, const clang::MacroDefinition&, const clang::SourceRange Range, const clang::MacroArgs* const Args)
{
    // Macro is not named for some reason.
    if(!MacroNameTok.isAnyIdentifier())
    { return; }

    if(MacroNameTok.getIdentifierInfo()->getName() == "EXPORT")
    {
        // Save Range into a queue.
    }
    else
    {
        return;
    }

    // If you want arguments you can declare the macro to have varargs and loop 
    // through the tokens, you can have any syntax you want as they're raw tokens.


    // /*   Get the argument list for this macro, because it's a
    //   varargs function all arguments are stored in argument 0. */
    // const ::clang::Token* token = Args->getUnexpArgument(0u);

    // // All tokens for the argument are stored in sequence.
    // for(; token->isNot(::clang::tok::eof); ++token)
    // {
    // }
}

在您的内部,您RecursiveAstVisitor可以实现将弹出队列顶部的访问者,并检查顶部宏是否在翻译单元之前。一个类型的 IIRC 访问者都是按照声明的顺序执行的,所以队列应该保持顺序。值得注意的是,一个类型的所有 Decl 都是按顺序访问的,因此在区分函数、变量和类时必须小心。

bool MyAstVisitor::VisitFunctionDecl(::clang::FunctionDecl* const function)
{
    if(::llvm::isa<::clang::CXXMethodDecl>(function))
    {
        // If you want to handle class member methods separately you 
        // can check here or implement `VisitCXXMethodDecl` and fast exit here.
    }

    if(ourExportTags.empty())
    {
        return true;
    }

    const ::clang::SourceLocation tagLoc = ourExportTags.front().getBegin();
    const ::clang::SourceLocation declLoc = function->getBeginLoc();

    if(getAstContext().getSourceManager().isBeforeInTranslationUnit(tagLoc, declLoc))
    {
        ourExportTags.pop_front();

        // Handle export;
    }

    return true;
}

编辑
我以前没有使用过 ASTMatchers,但是您可以通过编写匹配器、将所有声明存储到列表、根据位置排序,然后与原始导出标记队列进行比较来实现类似的结果。

DeclarationMatcher matcher = functionDecl().bind("funcDecl");


class MyFuncMatcher : public clang::ast_matchers::MatchFinder::MatchCallback
{
public:
    virtual void run(const clang::ast_matchers::MatchFinder::MatchResult& Result)
    {
        if(const FunctionDecl* func = Result.Nodes.getNodeAs<clang::FunctionDecl>("funcDecl"))
        {
            // Add to list for future processing
        }
    }
};

void joinTagsToDeclarations()
{
    // Sort the declaration list

    for(auto decl : myDeclList)
    {
        if(ourExportTags.empty())
        {
            break;
        }

        const ::clang::SourceLocation tagLoc = ourExportTags.front().getBegin();
        const ::clang::SourceLocation declLoc = decl->getBeginLoc();

        if(getAstContext().getSourceManager().isBeforeInTranslationUnit(tagLoc, declLoc))
        {
            ourExportTags.pop_front();

            // Handle export;
        }
    }
}
于 2022-01-06T17:21:27.610 回答