0

I have a single cpp file of about 100 lines with the following contents.

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

As you can see, the main function is quite simple, and calls a function twice at the end. The second time this function is called I get an assertion failure, which surprises me.

The contents of the file simple.cpp is

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

The output of this program on my machine is:

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)

The problem is: I want to execute more than one action on a CompilerInstance. What state do I have to reset in order to not get assertion failures?

To build it yourself you have to link with some static clang and llvm libraries. Here's the CMakeLists.txt file if interested:

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

I made a new directory path/to/llvm/tools/clang/tools/clangapitest and adjusted the CMakeLists.txt file in path/to/llvm/tools/clang/tools/CMakeLists.txt to have an extra line add_subdirectory(clangapitest).

rwols
  • 2,968
  • 2
  • 19
  • 26

1 Answers1

1

Well, I figured it out. In the doxygen documentation of CompilerInstance::ExecuteAction, it states that an invocation object and diagnostics object should have been initialized, and no other state (hence no source nor filemanager). So the following works:

SyntaxOnlyAction action;
instance.setSourceManager(nullptr);
instance.createDiagnostics();
if (instance.ExecuteAction(action))
{
    std::cout << "Action succeeded.\n";
}
else
{
    std::cout << "Action failed.\n";
}
rwols
  • 2,968
  • 2
  • 19
  • 26