Search code examples
c++clanglibtooling

Parse clang AST from `compile_commands.json` ignoring PCH


I'm trying to parse an AST of the whole project using a compilation database (compile_commands.json), but it contains pch files and commands related to generate those, resulting in errors during parsing: error: PCH file uses a newer PCH format that cannot be read 1 error generated.

The project has been configured (via CMake) and compiled using a different compiler, and the whole reason to make a tool instead of a plugin is not to be bound to a particular compiler. I might try an ad hoc solution to strip compile_commands.json from pch-related stuff, but I doubt it will work properly and worth the hustle.

The initialization is basically like this:

  auto options_parser =
      CommonOptionsParser::create(argc, argv, option_category);
  if (!options_parser) {
    llvm::errs() << options_parser.takeError();
    return -1;
  }

  ClangTool tool(options_parser->getCompilations(),
      options_parser->getCompilations().getAllFiles());

Is possible to configure the tool not to handle pch-related stuff?


Solution

  • I found the exact issue that has been solved for clangd, the corresponding changes can be found here.
    It boils down to setting certain fields for clang::CompilerInvocation - the class that contains all the state that is used to configure a CompilerInstance.

    An instance of CompilerInvocation is accessible via implementation of virtual bool clang::tooling::ToolAction::runInvocation. The pch-related options are accessible via CompilerInvocation::getPreprocessorOpts(). This code additionally disables reporting warnings

    struct custom_action : public clang::tooling::ToolAction {
      bool
      runInvocation(std::shared_ptr<clang::CompilerInvocation> inv,
                    clang::FileManager *files,
                    std::shared_ptr<clang::PCHContainerOperations> pch_cont_ops,
                    clang::DiagnosticConsumer *diag_cons) override {
    
        // createInvocationFromCommandLine sets DisableFree.
        inv->getFrontendOpts().DisableFree = false;
        inv->getLangOpts()->CommentOpts.ParseAllComments = true;
        inv->getLangOpts()->RetainCommentsFromSystemHeaders = true;
    
        [](auto &diag) {
          diag.Warnings.clear();
          diag.UndefPrefixes.clear();
          diag.Remarks.clear();
          diag.VerifyPrefixes.clear();
        }(inv->getDiagnosticOpts());
    
        [](auto &output) {
          // Disable any dependency outputting, we don't want to generate files or
          // write to stdout/stderr.
          output.ShowIncludesDest = clang::ShowIncludesDestination::None;
          output.OutputFile.clear();
          output.HeaderIncludeOutputFile.clear();
          output.DOTOutputFile.clear();
          output.ModuleDependencyOutputDir.clear();
        }(inv->getDependencyOutputOpts());
    
        [](auto &preproc) {
          // Disable any pch generation/usage operations. Since serialized preamble
          // format is unstable, using an incompatible one might result in
          // unexpected behaviours, including crashes.
          preproc.ImplicitPCHInclude.clear();
          preproc.PrecompiledPreambleBytes = {0, false};
          preproc.PCHThroughHeader.clear();
          preproc.PCHWithHdrStop = false;
          preproc.PCHWithHdrStopCreate = false;
        }(inv->getPreprocessorOpts());
    
        // your code here
        
        return true;
      }
    };
    

    One can configure all the needed adaptors and than call ClangTool::run(ToolAction*).