在clang的CompilerInstance上执行多个FrontendAction

问题描述:

我有一个包含以下内容的约100行的cpp文件。在clang的CompilerInstance上执行多个FrontendAction

#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"; 
    } 
} 

正如您所见,主函数非常简单,并且在最后调用一个函数两次。第二次调用这个函数时,我得到一个断言失败,这令我感到惊讶。

文件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和调整的CMakeLists.txt文件中path/to/llvm/tools/clang/tools/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"; 
}