我有一个大约100行的cpp文件,内容如下。

#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/FrontendActions.h>
#include <iostream>

// The filename that will be processed (twice).
static const char* FILENAME = "simple.cpp";

// System header locations, you may need to
// adjust these.
static const char* SYSTEM_HEADERS[] =
{
    "/usr/include/c++/5.4.0",
    "/usr/include/x86_64-linux-gnu/c++/5.4.0",
    "/usr/include/c++/5.4.0/backward",
    "/usr/local/lib/clang/4.0.0/include",
    "/usr/include/x86_64-linux-gnu",
    "/usr/include"
};

// Location for builtin headers. You may need to
// adjust this.
static const char* RESOURCE_DIR = "/usr/local/lib/clang/4.0.0";

// Uncomment this to see header search paths.
// #define PRINT_HEADER_SEARCH_PATHS

// Constructs a CompilerInvocation
// that must be fed to a CompilerInstance.
clang::CompilerInvocation* makeInvocation();

// Executes a single SyntaxOnlyAction on
// the given CompilerInstance.
void secondCallThisFunctionFails(clang::CompilerInstance& instance);

int main()
{
    using namespace clang;

    CompilerInstance instance;

    instance.createDiagnostics();

    instance.setInvocation(makeInvocation());
    instance.getFrontendOpts().Inputs.emplace_back
    (
        FILENAME,
        FrontendOptions::getInputKindForExtension(FILENAME)
    );

    // First call is OK.
    secondCallThisFunctionFails(instance);

    // Second call results in assertion failures.
    secondCallThisFunctionFails(instance);

    return 0;
}

clang::CompilerInvocation* makeInvocation()
{
    using namespace clang;
    auto invocation = new CompilerInvocation();

    invocation->TargetOpts->Triple = llvm::sys::getDefaultTargetTriple();
    invocation->setLangDefaults(
        *invocation->getLangOpts(),
        IK_CXX,
        llvm::Triple(invocation->TargetOpts->Triple),
        invocation->getPreprocessorOpts(),
        LangStandard::lang_cxx11);

    auto& headerSearchOpts = invocation->getHeaderSearchOpts();

    #ifdef PRINT_HEADER_SEARCH_PATHS
        headerSearchOpts.Verbose = true;
    #else
        headerSearchOpts.Verbose = false;
    #endif

    headerSearchOpts.UseBuiltinIncludes = true;
    headerSearchOpts.UseStandardSystemIncludes = true;
    headerSearchOpts.UseStandardCXXIncludes = true;
    headerSearchOpts.ResourceDir = RESOURCE_DIR;

    for (const auto sytemHeader : SYSTEM_HEADERS)
    {
        headerSearchOpts.AddPath(sytemHeader, frontend::System, false, false);
    }

    return invocation;
}

void secondCallThisFunctionFails(clang::CompilerInstance& instance)
{
    using namespace clang;
    SyntaxOnlyAction action;
    if (instance.ExecuteAction(action))
    {
        std::cout << "Action succeeded.\n";
    }
    else
    {
        std::cout << "Action failed.\n";
    }
}


如您所见,main函数非常简单,最后调用了两次函数。第二次调用此函数时,我得到断言失败,这使我感到惊讶。

文件simple.cpp的内容是

// test wether we actually configured C++11 or greater
#include <thread>
int main() { return 0; }


该程序在我的机器上的输出为:

Action succeeded.
clangapitest: ../tools/clang/lib/Basic/SourceManager.cpp:819: clang::FileID clang::SourceManager::getFileIDLoaded(unsigned int) const: Assertion `0 && "Invalid SLocOffset or bad function choice"' failed.
Aborted (core dumped)


问题是:我想在CompilerInstance上执行多个动作。为了不导致断言失败,我必须重置什么状态?

要自己构建它,您必须链接一些静态的clang和llvm库。如果感兴趣,这是CMakeLists.txt文件:

add_clang_executable(clangapitest clangapitest.cpp)
target_link_libraries(clangapitest clangFrontend)


我创建了一个新目录path/to/llvm/tools/clang/tools/clangapitest,并调整了path/to/llvm/tools/clang/tools/CMakeLists.txt中的CMakeLists.txt文件,使其具有多余的行add_subdirectory(clangapitest)

最佳答案

好吧,我知道了。在doxygen documentation of CompilerInstance::ExecuteAction中,它声明调用对象和诊断对象应该已经初始化,并且没有其他状态(因此没有源或文件管理器)。因此,以下工作原理:

SyntaxOnlyAction action;
instance.setSourceManager(nullptr);
instance.createDiagnostics();
if (instance.ExecuteAction(action))
{
    std::cout << "Action succeeded.\n";
}
else
{
    std::cout << "Action failed.\n";
}

关于c++ - 在clang中的CompilerInstance上执行多个FrontendAction,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40717746/

10-11 23:17