[Tooling] A new framework for executing clang frontend actions.

Summary:
This defines a `clang::tooling::ToolExecutor` interface that can be extended to support different execution plans including standalone execution on a given set of TUs or parallel execution on all TUs in a codebase.

In order to enable multiprocessing execution, tool actions are expected to output result into a `ToolResults` interface provided by executors. The `ToolResults` interface abstracts how results are stored e.g. in-memory for standalone executions or on-disk for large-scale execution.

New executors can be registered as `ToolExecutorPlugin`s via the `ToolExecutorPluginRegistry`. CLI tools can use `createExecutorFromCommandLineArgs` to create a specific registered executor according to the command-line arguments.

This patch also implements `StandaloneToolExecutor` which has the same behavior as the current `ClangTool` interface, i.e. execute frontend actions on a given set of TUs. At this point, it's simply a wrapper around `ClangTool` at this point.

This is still experimental but expected to replace the existing `ClangTool` interface so that specific tools would not need to worry about execution.

Reviewers: klimek, arphaman, hokein, sammccall

Reviewed By: klimek

Subscribers: cfe-commits, djasper, mgorny, omtcyfz

Differential Revision: https://reviews.llvm.org/D34272

llvm-svn: 316653
diff --git a/clang/lib/Tooling/ArgumentsAdjusters.cpp b/clang/lib/Tooling/ArgumentsAdjusters.cpp
index ac9fd3c..962ea45 100644
--- a/clang/lib/Tooling/ArgumentsAdjusters.cpp
+++ b/clang/lib/Tooling/ArgumentsAdjusters.cpp
@@ -96,6 +96,10 @@
 
 ArgumentsAdjuster combineAdjusters(ArgumentsAdjuster First,
                                    ArgumentsAdjuster Second) {
+  if (!First)
+    return Second;
+  if (!Second)
+    return First;
   return [First, Second](const CommandLineArguments &Args, StringRef File) {
     return Second(First(Args, File), File);
   };
diff --git a/clang/lib/Tooling/CMakeLists.txt b/clang/lib/Tooling/CMakeLists.txt
index 32a1935..ee681bb 100644
--- a/clang/lib/Tooling/CMakeLists.txt
+++ b/clang/lib/Tooling/CMakeLists.txt
@@ -11,11 +11,13 @@
   ArgumentsAdjusters.cpp
   CommonOptionsParser.cpp
   CompilationDatabase.cpp
+  Execution.cpp
   FileMatchTrie.cpp
   FixIt.cpp
   JSONCompilationDatabase.cpp
   Refactoring.cpp
   RefactoringCallbacks.cpp
+  StandaloneExecution.cpp
   Tooling.cpp
 
   DEPENDS
diff --git a/clang/lib/Tooling/CommonOptionsParser.cpp b/clang/lib/Tooling/CommonOptionsParser.cpp
index 9ad15cc..74ad4e8 100644
--- a/clang/lib/Tooling/CommonOptionsParser.cpp
+++ b/clang/lib/Tooling/CommonOptionsParser.cpp
@@ -147,10 +147,12 @@
   auto AdjustingCompilations =
       llvm::make_unique<ArgumentsAdjustingCompilations>(
           std::move(Compilations));
-  AdjustingCompilations->appendArgumentsAdjuster(
-      getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
-  AdjustingCompilations->appendArgumentsAdjuster(
+  Adjuster =
+      getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN);
+  Adjuster = combineAdjusters(
+      std::move(Adjuster),
       getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
+  AdjustingCompilations->appendArgumentsAdjuster(Adjuster);
   Compilations = std::move(AdjustingCompilations);
   return llvm::Error::success();
 }
diff --git a/clang/lib/Tooling/Execution.cpp b/clang/lib/Tooling/Execution.cpp
new file mode 100644
index 0000000..1a078ef
--- /dev/null
+++ b/clang/lib/Tooling/Execution.cpp
@@ -0,0 +1,89 @@
+//===- 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::cl::opt<std::string>
+    ExecutorName("executor", llvm::cl::desc("The name of the executor to use."),
+                 llvm::cl::init("standalone"));
+
+void InMemoryToolResults::addResult(StringRef Key, 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(StringRef Key, StringRef Value)> Callback) {
+  for (const auto &KV : KVResults) {
+    Callback(KV.first, KV.second);
+  }
+}
+
+void ExecutionContext::reportResult(StringRef Key, StringRef Value) {
+  Results->addResult(Key, Value);
+}
+
+llvm::Error
+ToolExecutor::execute(std::unique_ptr<FrontendActionFactory> Action) {
+  return execute(std::move(Action), ArgumentsAdjuster());
+}
+
+llvm::Error ToolExecutor::execute(std::unique_ptr<FrontendActionFactory> Action,
+                                  ArgumentsAdjuster Adjuster) {
+  std::vector<
+      std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
+      Actions;
+  Actions.emplace_back(std::move(Action), std::move(Adjuster));
+  return execute(Actions);
+}
+
+llvm::Expected<std::unique_ptr<ToolExecutor>>
+createExecutorFromCommandLineArgs(int &argc, const char **argv,
+                                  llvm::cl::OptionCategory &Category,
+                                  const char *Overview) {
+  auto OptionsParser =
+      CommonOptionsParser::create(argc, argv, Category, llvm::cl::ZeroOrMore,
+                                  /*Overview=*/nullptr);
+  if (!OptionsParser)
+    return OptionsParser.takeError();
+  for (auto I = ToolExecutorPluginRegistry::begin(),
+            E = ToolExecutorPluginRegistry::end();
+       I != E; ++I) {
+    if (I->getName() != ExecutorName) {
+      continue;
+    }
+    std::unique_ptr<ToolExecutorPlugin> Plugin(I->instantiate());
+    llvm::Expected<std::unique_ptr<ToolExecutor>> Executor =
+        Plugin->create(*OptionsParser);
+    if (!Executor) {
+      return llvm::make_error<llvm::StringError>(
+          llvm::Twine("Failed to create '") + I->getName() +
+              "': " + llvm::toString(Executor.takeError()) + "\n",
+          llvm::inconvertibleErrorCode());
+    }
+    return std::move(*Executor);
+  }
+  return llvm::make_error<llvm::StringError>(
+      llvm::Twine("Executor \"") + ExecutorName + "\" is not registered.",
+      llvm::inconvertibleErrorCode());
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/clang/lib/Tooling/StandaloneExecution.cpp b/clang/lib/Tooling/StandaloneExecution.cpp
new file mode 100644
index 0000000..aade40b
--- /dev/null
+++ b/clang/lib/Tooling/StandaloneExecution.cpp
@@ -0,0 +1,91 @@
+//===- lib/Tooling/Execution.cpp - Standalone clang action execution. -----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/StandaloneExecution.h"
+#include "clang/Tooling/ToolExecutorPluginRegistry.h"
+
+namespace clang {
+namespace tooling {
+
+static llvm::Error make_string_error(const llvm::Twine &Message) {
+  return llvm::make_error<llvm::StringError>(Message,
+                                             llvm::inconvertibleErrorCode());
+}
+
+const char *StandaloneToolExecutor::ExecutorName = "StandaloneToolExecutor";
+
+static ArgumentsAdjuster getDefaultArgumentsAdjusters() {
+  return combineAdjusters(
+      getClangStripOutputAdjuster(),
+      combineAdjusters(getClangSyntaxOnlyAdjuster(),
+                       getClangStripDependencyFileAdjuster()));
+}
+
+StandaloneToolExecutor::StandaloneToolExecutor(
+    const CompilationDatabase &Compilations,
+    llvm::ArrayRef<std::string> SourcePaths,
+    std::shared_ptr<PCHContainerOperations> PCHContainerOps)
+    : Tool(Compilations, SourcePaths), Context(&Results),
+      ArgsAdjuster(getDefaultArgumentsAdjusters()) {
+  // Use self-defined default argument adjusters instead of the default
+  // adjusters that come with the old `ClangTool`.
+  Tool.clearArgumentsAdjusters();
+}
+
+StandaloneToolExecutor::StandaloneToolExecutor(
+    CommonOptionsParser Options,
+    std::shared_ptr<PCHContainerOperations> PCHContainerOps)
+    : OptionsParser(std::move(Options)),
+      Tool(OptionsParser->getCompilations(), OptionsParser->getSourcePathList(),
+           PCHContainerOps),
+      Context(&Results), ArgsAdjuster(getDefaultArgumentsAdjusters()) {
+  Tool.clearArgumentsAdjusters();
+}
+
+llvm::Error StandaloneToolExecutor::execute(
+    llvm::ArrayRef<
+        std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
+        Actions) {
+  if (Actions.empty())
+    return make_string_error("No action to execute.");
+
+  if (Actions.size() != 1)
+    return make_string_error(
+        "Only support executing exactly 1 action at this point.");
+
+  auto &Action = Actions.front();
+  Tool.appendArgumentsAdjuster(Action.second);
+  Tool.appendArgumentsAdjuster(ArgsAdjuster);
+  if (int Ret = Tool.run(Action.first.get()))
+    return make_string_error("Failed to run action.");
+
+  return llvm::Error::success();
+}
+
+class StandaloneToolExecutorPlugin : public ToolExecutorPlugin {
+public:
+  llvm::Expected<std::unique_ptr<ToolExecutor>>
+  create(CommonOptionsParser &OptionsParser) override {
+    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", "Runs FrontendActions on a set of files provided "
+                    "via positional arguments.");
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp
index df9d7df..4fbfa4f 100644
--- a/clang/lib/Tooling/Tooling.cpp
+++ b/clang/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"
@@ -347,11 +348,7 @@
 }
 
 void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
-  if (ArgsAdjuster)
-    ArgsAdjuster =
-        combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
-  else
-    ArgsAdjuster = std::move(Adjuster);
+  ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
 }
 
 void ClangTool::clearArgumentsAdjusters() {