Search code examples
c++clangllvmlibclanglibtooling

Clang LibTooling doesn't process any source files


I'm trying to run a minimal LibTooling example, but must be missing something obvious, because my AST visitor isn't getting called at all, and any errors in the input files aren't getting diagnosed.

Here's my code:

#include <clang/Tooling/Tooling.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <llvm/Support/CommandLine.h>
#include <clang/AST/RecursiveASTVisitor.h>

#include <iostream>

static llvm::cl::OptionCategory my_options_category("my options");

struct MyClangAstVisitor : clang::RecursiveASTVisitor<MyClangAstVisitor>
{
    bool VisitNamedDecl(clang::NamedDecl *NamedDecl)
    {
        std::cout << "Found " << NamedDecl->getQualifiedNameAsString() << '\n';
        return true;
    }
};

struct MyClangAstConsumer : clang::ASTConsumer
{
    void HandleTranslationUnit(clang::ASTContext &ctx) override
    {
        std::cout << "HandleTranslationUnit()\n";
        MyClangAstVisitor{}.TraverseDecl(ctx.getTranslationUnitDecl());
    }
};

struct MyClangFrontendAction : clang::FrontendAction
{
    std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef InFile) override
    {
        (void)CI;
        (void)InFile;
        std::cout << "CreateASTConsumer()\n";
        return std::make_unique<MyClangAstConsumer>();
    }

    void ExecuteAction() override
    {
        std::cout << "ExecuteAction()\n";
    }

    bool usesPreprocessorOnly() const override {return false;}
};

int main(int argc, char **argv)
{
    auto option_parser_ex = clang::tooling::CommonOptionsParser::create(argc, const_cast<const char **>(argv), my_options_category);
    if (!option_parser_ex)
    {
        llvm::errs() << option_parser_ex.takeError();
        return 1;
    }

    clang::tooling::CommonOptionsParser &option_parser = option_parser_ex.get();

    auto a = option_parser.getSourcePathList();
    for (auto x : a)
        std::cout << "Will compile: " << x << '\n';

    clang::tooling::ClangTool tool(option_parser.getCompilations(), option_parser.getSourcePathList());

    return tool.run(clang::tooling::newFrontendActionFactory<MyClangFrontendAction>().get());
}

I run it as my_tool input.cpp --, and it prints

Will compile: input.cpp
CreateASTConsumer()
ExecuteAction()

And exits with exit code 0.

Hovewer MyClangAstConsumer::HandleTranslationUnit() doesn't get called (both according to logs and according to the debugger), so MyClangAstVisitor doesn't get used.

The input file I used is just class A {};. Any errors in the input file don't seem to be diagnosed (same output regardless), but if I specify an incorrect filename, I do get an error.

My CMake file is:

cmake_minimum_required(VERSION 3.10)

project(my_tool)
add_executable(my_tool src/main.cpp)
find_package(Clang REQUIRED)
target_include_directories(my_tool PRIVATE ${LLVM_INCLUDE_DIR} ${CLANG_INCLUDE_DIR})
target_link_libraries(my_tool PRIVATE clangTooling)

I'm building this on Windows in MSYS2 (in UCRT64 environment). Also tried Ubuntu 22.04 with the same result.


Solution

  • FrontendAction is a fairly generic base class used by a variety of frontend actions, not all of which necessarily create an AST.

    If you derive from FrontendAction directly, you need to write the code that e.g. triggers creating an AST in the ExecuteAction() override.

    Alternatively, there is a derived class called ASTFrontendAction which handles that (it implements ExecuteAction() for you), which you can derive from and only override CreateASTConsumer:

    struct MyClangFrontendAction : clang::ASTFrontendAction
    {
        std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef InFile) override
        {
            (void)CI;
            (void)InFile;
            std::cout << "CreateASTConsumer()\n";
            return std::make_unique<MyClangAstConsumer>();
        }
    };
    

    This should work for your use case.