ioeric updated this revision to Diff 119122.
ioeric marked 5 inline comments as done.
ioeric added a comment.

- Address review comments.


https://reviews.llvm.org/D34272

Files:
  include/clang/Tooling/CommonOptionsParser.h
  include/clang/Tooling/Execution.h
  include/clang/Tooling/ToolExecutorPluginRegistry.h
  include/clang/Tooling/Tooling.h
  lib/Tooling/CMakeLists.txt
  lib/Tooling/CommonOptionsParser.cpp
  lib/Tooling/Execution.cpp
  lib/Tooling/Tooling.cpp
  unittests/Tooling/ToolingTest.cpp

Index: unittests/Tooling/ToolingTest.cpp
===================================================================
--- unittests/Tooling/ToolingTest.cpp
+++ unittests/Tooling/ToolingTest.cpp
@@ -7,14 +7,18 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "gmock/gmock.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclGroup.h"
+#include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendAction.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Execution.h"
+#include "clang/Tooling/ToolExecutorPluginRegistry.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Config/llvm-config.h"
@@ -60,6 +64,60 @@
  private:
   bool * const FoundTopLevelDecl;
 };
+
+// This traverses the AST and outputs function name as key and "1" as value for
+// each function declaration.
+class ASTConsumerWithResult
+    : public ASTConsumer,
+      public RecursiveASTVisitor<ASTConsumerWithResult> {
+public:
+  using ASTVisitor = RecursiveASTVisitor<ASTConsumerWithResult>;
+
+  explicit ASTConsumerWithResult(ToolResults *Results) : Results(Results) {
+    assert(Results != nullptr);
+  }
+
+  void HandleTranslationUnit(clang::ASTContext &Context) override {
+    TraverseDecl(Context.getTranslationUnitDecl());
+  }
+
+  bool TraverseFunctionDecl(clang::FunctionDecl *Decl) {
+    Results->addResult(Decl->getNameAsString(), "1");
+    return ASTVisitor::TraverseFunctionDecl(Decl);
+  }
+
+private:
+  ToolResults *const Results;
+};
+
+class ReportResultAction : public ASTFrontendAction {
+public:
+  explicit ReportResultAction(ToolResults *Results) : Results(Results) {
+    assert(Results != nullptr);
+  }
+
+protected:
+  std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(clang::CompilerInstance &compiler,
+                    llvm::StringRef /* dummy */) override {
+    std::unique_ptr<clang::ASTConsumer> ast_consumer{
+        new ASTConsumerWithResult(Results)};
+    return ast_consumer;
+  }
+
+private:
+  ToolResults *const Results;
+};
+
+class ReportResultActionFactory : public FrontendActionFactory {
+public:
+  ReportResultActionFactory(ToolResults *Results) : Results(Results) {}
+  FrontendAction *create() override { return new ReportResultAction(Results); }
+
+private:
+  ToolResults *const Results;
+};
+
 } // end namespace
 
 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
@@ -533,5 +591,170 @@
 }
 #endif
 
+inline llvm::Error make_string_error(const llvm::Twine &Message) {
+  return llvm::make_error<llvm::StringError>(Message,
+                                             llvm::inconvertibleErrorCode());
+}
+
+class TestToolExecutor : public ToolExecutor {
+public:
+  static const char *ExecutorName;
+
+  TestToolExecutor(std::unique_ptr<CommonOptionsParser> Options)
+      : OptionsParser(std::move(Options)) {}
+
+  llvm::StringRef getExecutorName() const override { return ExecutorName; }
+
+  llvm::Error execute(const ExecutionConfig &) override {
+    return llvm::Error::success();
+  }
+
+  ToolResults *getToolResults() override { return nullptr; };
+
+  llvm::ArrayRef<std::string> getSourcePaths() const {
+    return OptionsParser->getSourcePathList();
+  }
+
+  void mapVirtualFile(StringRef FilePath, StringRef Content) override {
+    VFS[FilePath] = Content;
+  }
+
+private:
+  std::unique_ptr<CommonOptionsParser> OptionsParser;
+  std::string SourcePaths;
+  std::map<std::string, std::string> VFS;
+};
+
+const char *TestToolExecutor::ExecutorName = "TestToolExecutor";
+
+class TestToolExecutorPlugin : public ToolExecutorPlugin {
+public:
+  llvm::Expected<std::unique_ptr<ToolExecutor>>
+  create(int &argc, const char **argv,
+         llvm::cl::OptionCategory &Category) override {
+    // FIXME: Using static option here because `opt`s could not be ungirestered
+    // properly. The static option seems to work fine for now.
+    static llvm::cl::opt<bool> TestExecutor(
+        "test_executor", llvm::cl::desc("Use TestToolExecutor"));
+    auto OptionsParser = llvm::make_unique<CommonOptionsParser>(
+        argc, argv, Category, llvm::cl::OneOrMore, /*Overview=*/nullptr,
+        /*ExitOnError=*/false);
+    if (OptionsParser->hasError())
+      return make_string_error("[TestToolExecutorPlugin] " +
+                               OptionsParser->getErrorMessage());
+    if (!TestExecutor)
+      return make_string_error(
+          "[TestToolExecutorPlugin] --test_executor is not set.");
+    return llvm::make_unique<TestToolExecutor>(std::move(OptionsParser));
+  }
+};
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the plugin.
+extern volatile int ToolExecutorPluginAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED TestToolExecutorPluginAnchorDest =
+    ToolExecutorPluginAnchorSource;
+
+static ToolExecutorPluginRegistry::Add<TestToolExecutorPlugin>
+    X("test-executor", "Plugin for TestToolExecutor.");
+
+TEST(CreateToolExecutorTest, FailedCreateExecutorUndefinedFlag) {
+  llvm::cl::ResetCommandLineParser();
+  llvm::cl::OptionCategory TestCategory("tooling-test options");
+  std::vector<const char *> argv = {"prog", "--fake_flag_no_no_no", "f"};
+  int argc = argv.size();
+  auto Executor =
+      createExecutorFromCommandLineArgs(argc, &argv[0], TestCategory);
+  ASSERT_FALSE((bool)Executor);
+  llvm::consumeError(Executor.takeError());
+}
+
+TEST(CreateToolExecutorTest, RegisterFlagsBeforeReset) {
+  llvm::cl::ResetCommandLineParser();
+
+  llvm::cl::opt<std::string> BeforeReset(
+      "before_reset", llvm::cl::desc("Defined before reset."),
+      llvm::cl::init(""));
+  llvm::cl::OptionCategory TestCategory("tooling-test options");
+
+  llvm::cl::ResetAllOptionOccurrences();
+
+  std::vector<const char *> argv = {"prog", "--before_reset=set", "f"};
+  int argc = argv.size();
+  auto Executor =
+      createExecutorFromCommandLineArgs(argc, &argv[0], TestCategory);
+  ASSERT_TRUE((bool)Executor);
+  EXPECT_EQ(BeforeReset, "set");
+}
+
+TEST(CreateToolExecutorTest, CreateStandaloneToolExecutor) {
+  llvm::cl::ResetCommandLineParser();
+  llvm::cl::OptionCategory TestCategory("tooling-test options");
+  std::vector<const char *> argv = {"prog", "standalone.cpp"};
+  int argc = argv.size();
+  auto Executor =
+      createExecutorFromCommandLineArgs(argc, &argv[0], TestCategory);
+  ASSERT_TRUE((bool)Executor);
+  EXPECT_EQ(Executor->get()->getExecutorName(),
+            StandaloneToolExecutor::ExecutorName);
+}
+
+TEST(CreateToolExecutorTest, CreateTestToolExecutor) {
+  llvm::cl::ResetCommandLineParser();
+  llvm::cl::OptionCategory TestCategory("tooling-test options");
+  std::vector<const char *> argv = {"prog", "test.cpp", "--test_executor"};
+  int argc = argv.size();
+  auto Executor =
+      createExecutorFromCommandLineArgs(argc, &argv[0], TestCategory);
+  ASSERT_TRUE((bool)Executor);
+  EXPECT_EQ(Executor->get()->getExecutorName(), TestToolExecutor::ExecutorName);
+}
+
+TEST(StandaloneToolTest, SynctaxOnlyActionOnSimpleCode) {
+  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
+  StandaloneToolExecutor Executor(Compilations,
+                                  std::vector<std::string>(1, "/a.cc"));
+  Executor.mapVirtualFile("/a.cc", "int x = 0;");
+
+  auto Err = Executor.execute(
+      ExecutionConfig(newFrontendActionFactory<SyntaxOnlyAction>()));
+  ASSERT_TRUE(!Err);
+}
+
+TEST(StandaloneToolTest, SimpleAction) {
+  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
+  StandaloneToolExecutor Executor(Compilations,
+                                  std::vector<std::string>(1, "/a.cc"));
+  Executor.mapVirtualFile("/a.cc", "int x = 0;");
+
+  ToolResults *Results = Executor.getToolResults();
+
+  auto Err = Executor.execute(ExecutionConfig(
+      std::unique_ptr<ToolAction>(new ReportResultActionFactory(Results))));
+  ASSERT_TRUE(!Err);
+  auto KVs = Results->AllKVResults();
+  ASSERT_EQ(KVs.size(), 0u);
+}
+
+TEST(StandaloneToolTest, SimpleActionWithResult) {
+  FixedCompilationDatabase Compilations("/", std::vector<std::string>());
+  StandaloneToolExecutor Executor(Compilations,
+                                  std::vector<std::string>(1, "/a.cc"));
+  Executor.mapVirtualFile("/a.cc", "int x = 0; void f() {}");
+
+  ToolResults *Results = Executor.getToolResults();
+
+  auto Err = Executor.execute(ExecutionConfig(
+      std::unique_ptr<ToolAction>(new ReportResultActionFactory(Results))));
+  ASSERT_TRUE(!Err);
+  auto KVs = Results->AllKVResults();
+  ASSERT_EQ(KVs.size(), 1u);
+  EXPECT_EQ("f", KVs[0].first);
+  EXPECT_EQ("1", KVs[0].second);
+
+  Results->forEachResult(
+      [](llvm::StringRef, llvm::StringRef Value) { EXPECT_EQ("1", Value); });
+}
+
 } // end namespace tooling
 } // end namespace clang
Index: lib/Tooling/Tooling.cpp
===================================================================
--- lib/Tooling/Tooling.cpp
+++ lib/Tooling/Tooling.cpp
@@ -29,6 +29,7 @@
 #include "llvm/Config/llvm-config.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Host.h"
Index: lib/Tooling/Execution.cpp
===================================================================
--- /dev/null
+++ lib/Tooling/Execution.cpp
@@ -0,0 +1,134 @@
+//===- lib/Tooling/Execution.cpp - Implements tool execution framework. ---===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Execution.h"
+#include "clang/Tooling/ToolExecutorPluginRegistry.h"
+#include "clang/Tooling/Tooling.h"
+
+LLVM_INSTANTIATE_REGISTRY(clang::tooling::ToolExecutorPluginRegistry)
+
+namespace clang {
+namespace tooling {
+
+static llvm::Error make_string_error(const llvm::Twine &Message) {
+  return llvm::make_error<llvm::StringError>(Message,
+                                             llvm::inconvertibleErrorCode());
+}
+
+void InMemoryToolResults::addResult(llvm::StringRef Key,
+                                    llvm::StringRef Value) {
+  KVResults.push_back({Key.str(), Value.str()});
+}
+
+std::vector<std::pair<std::string, std::string>>
+InMemoryToolResults::AllKVResults() {
+  return KVResults;
+}
+
+void InMemoryToolResults::forEachResult(
+    llvm::function_ref<void(llvm::StringRef Key, llvm::StringRef Value)>
+        Callback) {
+  for (const auto &KV : KVResults) {
+    Callback(KV.first, KV.second);
+  }
+}
+
+ExecutionConfig::ExecutionConfig(std::unique_ptr<ToolAction> Action) {
+  Actions.push_back(std::make_pair(nullptr, std::move(Action)));
+}
+
+llvm::Expected<std::unique_ptr<ToolExecutor>>
+createExecutorFromCommandLineArgs(int &argc, const char **argv,
+                                  llvm::cl::OptionCategory &Category,
+                                  const char *Overview) {
+  std::string ErrorMessage;
+  llvm::raw_string_ostream OS(ErrorMessage);
+  // Find the first executor plugin that matches the given command-line
+  // arguments.
+  for (auto I = ToolExecutorPluginRegistry::begin(),
+            E = ToolExecutorPluginRegistry::end();
+       I != E; ++I) {
+    std::unique_ptr<ToolExecutorPlugin> Plugin(I->instantiate());
+    std::vector<std::string> args(argv, argv + argc);
+    int arg_num = argc;
+    llvm::Expected<std::unique_ptr<ToolExecutor>> Executor =
+        Plugin->create(arg_num, argv, Category);
+    if (!Executor) {
+      OS << "Failed to create '" << I->getName()
+         << "': " << llvm::toString(Executor.takeError()) << "\n";
+      llvm::cl::ResetAllOptionOccurrences();
+      continue;
+    }
+    argc = arg_num;
+    return std::move(*Executor);
+  }
+  OS.flush();
+  return make_string_error(ErrorMessage);
+}
+
+const char *StandaloneToolExecutor::ExecutorName = "StandaloneToolExecutor";
+
+StandaloneToolExecutor::StandaloneToolExecutor(
+    const CompilationDatabase &Compilations,
+    llvm::ArrayRef<std::string> SourcePaths,
+    std::shared_ptr<PCHContainerOperations> PCHContainerOps)
+    : Tool(Compilations, SourcePaths) {}
+
+StandaloneToolExecutor::StandaloneToolExecutor(
+    std::unique_ptr<CommonOptionsParser> Options,
+    std::shared_ptr<PCHContainerOperations> PCHContainerOps)
+    : OptionsParser(std::move(Options)),
+      Tool(OptionsParser->getCompilations(), OptionsParser->getSourcePathList(),
+           PCHContainerOps) {}
+
+llvm::Error StandaloneToolExecutor::execute(const ExecutionConfig &Config) {
+  if (Config.Actions.empty())
+    return make_string_error("No action to execute.");
+
+  if (Config.Actions.size() != 1)
+    return make_string_error(
+        "Only support executing exactly 1 ToolAction at this point.");
+
+  auto &Action = Config.Actions.front();
+  if (Action.first)
+    Tool.appendArgumentsAdjuster(Action.first);
+  if (int Ret = Tool.run(Action.second.get()))
+    return make_string_error("Failed to run ToolAction.");
+
+  return llvm::Error::success();
+}
+
+class StandaloneToolExecutorPlugin : public ToolExecutorPlugin {
+public:
+  llvm::Expected<std::unique_ptr<ToolExecutor>>
+  create(int &argc, const char **argv,
+         llvm::cl::OptionCategory &Category) override {
+    auto OptionsParser = llvm::make_unique<CommonOptionsParser>(
+        argc, argv, Category, llvm::cl::OneOrMore, /*Overview=*/nullptr,
+        /*ExitOnError=*/false);
+    if (OptionsParser->hasError())
+      return make_string_error("[StandaloneToolExecutorPlugin] " +
+                               OptionsParser->getErrorMessage());
+    if (OptionsParser->getSourcePathList().empty())
+      return make_string_error(
+          "[StandaloneToolExecutorPlugin] No positional argument found.");
+    return llvm::make_unique<StandaloneToolExecutor>(std::move(OptionsParser));
+  }
+};
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the plugin.
+volatile int ToolExecutorPluginAnchorSource = 0;
+
+static ToolExecutorPluginRegistry::Add<StandaloneToolExecutorPlugin>
+    X("standalone-executor",
+      "Runs ToolActions on a set of files provided via positional arguments.");
+
+} // end namespace tooling
+} // end namespace clang
Index: lib/Tooling/CommonOptionsParser.cpp
===================================================================
--- lib/Tooling/CommonOptionsParser.cpp
+++ lib/Tooling/CommonOptionsParser.cpp
@@ -83,7 +83,8 @@
 
 CommonOptionsParser::CommonOptionsParser(
     int &argc, const char **argv, cl::OptionCategory &Category,
-    llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview) {
+    llvm::cl::NumOccurrencesFlag OccurrencesFlag, const char *Overview,
+    bool ExitOnError) {
   static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden,
                             cl::sub(*cl::AllSubCommands));
 
@@ -105,14 +106,21 @@
       cl::desc("Additional argument to prepend to the compiler command line"),
       cl::cat(Category), cl::sub(*cl::AllSubCommands));
 
+  cl::ResetAllOptionOccurrences();
+
   cl::HideUnrelatedOptions(Category);
 
-  std::string ErrorMessage;
-  Compilations =
-      FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
+  std::string Err;
+  Compilations = FixedCompilationDatabase::loadFromCommandLine(argc, argv, Err);
+  llvm::raw_string_ostream OS(ErrorMessage);
   if (!Compilations && !ErrorMessage.empty())
-    llvm::errs() << ErrorMessage;
-  cl::ParseCommandLineOptions(argc, argv, Overview);
+    OS << Err << "\n";
+  HasError = !cl::ParseCommandLineOptions(argc, argv, Overview, &OS);
+  assert(!(ExitOnError && HasError) &&
+         "Command parsing failed in common option parser.");
+  // Stop initializing if command-line option parsing failed.
+  if (HasError)
+    return;
   cl::PrintOptionValues();
 
   SourcePathList = SourcePaths;
Index: lib/Tooling/CMakeLists.txt
===================================================================
--- lib/Tooling/CMakeLists.txt
+++ lib/Tooling/CMakeLists.txt
@@ -11,6 +11,7 @@
   ArgumentsAdjusters.cpp
   CommonOptionsParser.cpp
   CompilationDatabase.cpp
+  Execution.cpp
   FileMatchTrie.cpp
   FixIt.cpp
   JSONCompilationDatabase.cpp
Index: include/clang/Tooling/Tooling.h
===================================================================
--- include/clang/Tooling/Tooling.h
+++ include/clang/Tooling/Tooling.h
@@ -31,18 +31,21 @@
 #define LLVM_CLANG_TOOLING_TOOLING_H
 
 #include "clang/AST/ASTConsumer.h"
-#include "clang/Frontend/PCHContainerOperations.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Driver/Util.h"
 #include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/PCHContainerOperations.h"
 #include "clang/Lex/ModuleLoader.h"
 #include "clang/Tooling/ArgumentsAdjusters.h"
+#include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Option/Option.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Registry.h"
 #include <memory>
 #include <string>
 #include <vector>
@@ -337,7 +340,9 @@
   /// The file manager is shared between all translation units.
   FileManager &getFiles() { return *Files; }
 
- private:
+  llvm::ArrayRef<std::string> getSourcePaths() const { return SourcePaths; }
+
+private:
   const CompilationDatabase &Compilations;
   std::vector<std::string> SourcePaths;
   std::shared_ptr<PCHContainerOperations> PCHContainerOps;
Index: include/clang/Tooling/ToolExecutorPluginRegistry.h
===================================================================
--- /dev/null
+++ include/clang/Tooling/ToolExecutorPluginRegistry.h
@@ -0,0 +1,24 @@
+//===--- ToolExecutorPluginRegistry.h - -------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_TOOLEXECUTORPLUGINREGISTRY_H
+#define LLVM_CLANG_TOOLING_TOOLEXECUTORPLUGINREGISTRY_H
+
+#include "clang/Tooling/Execution.h"
+#include "llvm/Support/Registry.h"
+
+namespace clang {
+namespace tooling {
+
+typedef llvm::Registry<ToolExecutorPlugin> ToolExecutorPluginRegistry;
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_TOOLEXECUTORPLUGINREGISTRY_H
Index: include/clang/Tooling/Execution.h
===================================================================
--- /dev/null
+++ include/clang/Tooling/Execution.h
@@ -0,0 +1,198 @@
+//===--- Execution.h - Executing clang frontend actions -*- C++ ---------*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines framework for executing clang frontend actions.
+//
+//  The framework can be extended to support different execution plans including
+//  standalone execution on the given TUs or parallel execution on all TUs in
+//  the codebase.
+//
+//  In order to enable multiprocessing execution, tool actions are expected to
+//  output result into the ToolResults provided by the executor. The
+//  `ToolResults` is an interface that abstracts how results are stored e.g.
+//  in-memory for standalone execution or on-disk for large-scale execution.
+//
+//  New executors can be registered as ToolExecutorPlugins via the
+//  `ToolExecutorPluginRegistry`. CLI tools can use
+//  `createExecutorFromCommandLineArgs` to create a specific registered executor
+//  according to the command-line arguments.
+//
+//  This is still experimental but expected to replace the existing `ClangTool`
+//  interface.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_EXECUTION_H
+#define LLVM_CLANG_TOOLING_EXECUTION_H
+
+#include "clang/Tooling/Tooling.h"
+
+namespace clang {
+namespace tooling {
+
+/// \brief An abstraction for the result of a tool execution. For example, the
+/// underlying result can be in-memory or on-disk.
+///
+/// Results should be string key-value pairs. For example, a refactoring tool
+/// can use source location as key and a replacement in YAML format as value.
+class ToolResults {
+public:
+  virtual ~ToolResults() {}
+  virtual void addResult(llvm::StringRef Key, llvm::StringRef Value) = 0;
+  virtual std::vector<std::pair<std::string, std::string>> AllKVResults() = 0;
+  virtual void forEachResult(
+      llvm::function_ref<void(llvm::StringRef Key, llvm::StringRef Value)>
+          Callback) = 0;
+};
+
+/// \brief Specifies the actions to be executed in a tool execution and the
+/// compilation arguments to be used.
+struct ExecutionConfig {
+  ExecutionConfig() = default;
+
+  // Config for executing a single `Action` with the default configuration e.g.
+  // `ClangStripOutputAdjuster` and `ClangSyntaxOnlyAdjuster`.
+  explicit ExecutionConfig(std::unique_ptr<ToolAction> Action);
+
+  // Config for executing multiple actions that use customized argument
+  // adjusters in addition to the default one.
+  std::vector<std::pair<ArgumentsAdjuster, std::unique_ptr<ToolAction>>>
+      Actions;
+};
+
+/// \brief Interface for executing clang frontend actions.
+///
+/// This can be extended to support running tool actions in different
+/// execution mode, e.g. on a specific set of TUs or many TUs in parallel.
+///
+///  New executors can be registered as ToolExecutorPlugins via the
+///  `ToolExecutorPluginRegistry`. CLI tools can use
+///  `createExecutorFromCommandLineArgs` to create a specific registered
+///  executor according to the command-line arguments.
+class ToolExecutor {
+public:
+  virtual ~ToolExecutor() {}
+
+  /// \brief Returns the name of a specific executor.
+  virtual llvm::StringRef getExecutorName() const = 0;
+
+  /// \brief Executes actions specified in the execution configuration.
+  virtual llvm::Error execute(const ExecutionConfig &Config) = 0;
+
+  /// \brief Returns a reference of the tool results.
+  virtual ToolResults *getToolResults() = 0;
+
+  /// \brief Map a virtual file to be used while running the tool.
+  ///
+  /// \param FilePath The path at which the content will be mapped.
+  /// \param Content A buffer of the file's content.
+  virtual void mapVirtualFile(StringRef FilePath, StringRef Content) = 0;
+};
+
+class InMemoryToolResults : public ToolResults {
+public:
+  void addResult(llvm::StringRef Key, llvm::StringRef Value) override;
+  std::vector<std::pair<std::string, std::string>> AllKVResults() override;
+  void forEachResult(llvm::function_ref<void(llvm::StringRef Key,
+                                             llvm::StringRef Value)>) override;
+
+private:
+  std::vector<std::pair<std::string, std::string>> KVResults;
+};
+
+/// \brief A stand alone executor that runs FrontendActions on a given set of
+/// TUs in sequence.
+class StandaloneToolExecutor : public ToolExecutor {
+public:
+  static const char *ExecutorName;
+
+  /// \brief Init with \p CompilationDatabase and the paths of all files to be
+  /// proccessed.
+  StandaloneToolExecutor(
+      const CompilationDatabase &Compilations,
+      llvm::ArrayRef<std::string> SourcePaths,
+      std::shared_ptr<PCHContainerOperations> PCHContainerOps =
+          std::make_shared<PCHContainerOperations>());
+
+  /// \brief Init with \p CommonOptionsParser. This is expected to be used by
+  /// `createExecutorFromCommandLineArgs` based on commandline options.
+  ///
+  /// The executor takes ownership of \p Options.
+  StandaloneToolExecutor(
+      std::unique_ptr<CommonOptionsParser> Options,
+      std::shared_ptr<PCHContainerOperations> PCHContainerOps =
+          std::make_shared<PCHContainerOperations>());
+
+  llvm::StringRef getExecutorName() const override { return ExecutorName; }
+
+  llvm::Error execute(const ExecutionConfig &Config) override;
+
+  /// \brief Set a \c DiagnosticConsumer to use during parsing.
+  void setDiagnosticConsumer(DiagnosticConsumer *DiagConsumer) {
+    Tool.setDiagnosticConsumer(DiagConsumer);
+  }
+
+  /// \brief Returns a reference to the tool results container. The results will
+  /// be populated after the execution.
+  ToolResults *getToolResults() override { return &Results; };
+
+  llvm::ArrayRef<std::string> getSourcePaths() const {
+    return Tool.getSourcePaths();
+  }
+
+  void mapVirtualFile(StringRef FilePath, StringRef Content) override {
+    Tool.mapVirtualFile(FilePath, Content);
+  }
+
+  /// \brief Returns the file manager used in the tool.
+  ///
+  /// The file manager is shared between all translation units.
+  FileManager &getFiles() { return Tool.getFiles(); }
+
+private:
+  // Used to store the parser when the executor is initialized with parser.
+  std::unique_ptr<CommonOptionsParser> OptionsParser;
+  // FIXME: The standalone executor is currently just a wrapper of `ClangTool`.
+  // Merge `ClangTool` implementation into the this.
+  ClangTool Tool;
+  InMemoryToolResults Results;
+};
+
+/// \brief Interface for factories that create speicifc executors based on the
+/// commandline arguments. This is also used as a plugin to be registered into
+/// ToolExecutorPluginRegistry.
+class ToolExecutorPlugin {
+public:
+  virtual ~ToolExecutorPlugin() {}
+
+  /// \brief Create a ToolExecutor based on the commandline arguments. If the
+  /// set of commandline arguments is not supported by the ToolExecutor, this
+  /// returns an error.
+  /// Expected commandline options should be registered in the implementation.
+  virtual llvm::Expected<std::unique_ptr<ToolExecutor>>
+  create(int &argc, const char **argv, llvm::cl::OptionCategory &Category) = 0;
+};
+
+/// \brief This creates a ToolExecutor that is in the global registry and
+/// supports the given commandline arguments.
+///
+/// This tries creating all registered executors from the given command-line
+/// arguments and returns the first executor that matches all arguments.
+///
+/// By default, this creates a `StandaloneToolExecutor` if not other tool
+/// flags are used.
+llvm::Expected<std::unique_ptr<ToolExecutor>>
+createExecutorFromCommandLineArgs(int &argc, const char **argv,
+                                  llvm::cl::OptionCategory &Category,
+                                  const char *Overview = nullptr);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_EXECUTION_H
Index: include/clang/Tooling/CommonOptionsParser.h
===================================================================
--- include/clang/Tooling/CommonOptionsParser.h
+++ include/clang/Tooling/CommonOptionsParser.h
@@ -87,11 +87,13 @@
   ///
   /// It also allows calls to set the required number of positional parameters.
   ///
-  /// This constructor exits program in case of error.
+  /// If \p ExitOnError is set to true (default), the constructor exits program
+  /// in case of error; otherwise, this sets the error flag and stores error
+  /// messages.
   CommonOptionsParser(int &argc, const char **argv,
                       llvm::cl::OptionCategory &Category,
                       llvm::cl::NumOccurrencesFlag OccurrencesFlag,
-                      const char *Overview = nullptr);
+                      const char *Overview = nullptr, bool ExitOnError = true);
 
   /// Returns a reference to the loaded compilations database.
   CompilationDatabase &getCompilations() {
@@ -103,9 +105,15 @@
     return SourcePathList;
   }
 
+  bool hasError() const { return HasError; }
+
+  const std::string &getErrorMessage() const { return ErrorMessage; }
+
   static const char *const HelpMessage;
 
 private:
+  bool HasError;
+  std::string ErrorMessage;
   std::unique_ptr<CompilationDatabase> Compilations;
   std::vector<std::string> SourcePathList;
 };
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to