sammccall created this revision.
Herald added a project: All.
sammccall requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

The tooling APIs have a lot of extension points for customization:
e.g. Executor, FrontendActionFactory, FrontendAction, ASTConsumer.
This customization is often not needed, and makes the APIs unergonomic.

This change introduces some shortcuts to bypass them, while attempting
not to add more concepts/complexity to the APIs.

1. main() entrypoint

Today tools are expected to create an executor (which can fail), then
use it to execute their action (which can report errors).
Errors are reported with llvm::Error, which is not what main() needs
(exit codes) but must be handled.

This patch adds executeFromCommandLineArgs(), which wraps this in a
single function which returns an exit code.

(It also tweaks ToolExecutor's execute() API to avoid replicating several
overloads here).

2. Skipping over FrontendAction to ASTConsumer

Tools that override ASTConsumer must provide two factory indirections:

  FrontendActionFactory -> FrontendAction -> ASTConsumer.

These are sometimes meaningful, but often not.
newFrontendActionFactory<ActionT>() already lets you skip the first.

Now newFrontendActionFactory<ConsumerT>() lets you skip the second, too.

3. Implementing simple actions as lambdas.

A large fraction of tools simply want to consume the final AST and do
something with it. Even ASTConsumer is too heavyweight an abstraction.

This patch adds consumeASTs(), which is a FrontendActionFactory that
calls a function once for each parsed AST:

  Exec->execute(consumeASTs([&](ASTContext &Ctx) {
    ... process AST ...
  }));

Bonus: through lambda captures, state can be easily shared across TUs.
(This otherwise tends to require plumbing, or use of global variables)


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D155361

Files:
  clang/include/clang/Tooling/AllTUsExecution.h
  clang/include/clang/Tooling/Execution.h
  clang/include/clang/Tooling/StandaloneExecution.h
  clang/include/clang/Tooling/Tooling.h
  clang/lib/Tooling/AllTUsExecution.cpp
  clang/lib/Tooling/Execution.cpp
  clang/lib/Tooling/StandaloneExecution.cpp
  clang/lib/Tooling/Tooling.cpp
  clang/unittests/Tooling/ExecutionTest.cpp
  clang/unittests/Tooling/ToolingTest.cpp

Index: clang/unittests/Tooling/ToolingTest.cpp
===================================================================
--- clang/unittests/Tooling/ToolingTest.cpp
+++ clang/unittests/Tooling/ToolingTest.cpp
@@ -10,6 +10,7 @@
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclGroup.h"
+#include "clang/AST/DeclarationName.h"
 #include "clang/Driver/Compilation.h"
 #include "clang/Driver/Driver.h"
 #include "clang/Frontend/ASTUnit.h"
@@ -511,6 +512,18 @@
 }
 #endif
 
+TEST(newFrontendActionFactory, ConsumeASTs) {
+  FixedCompilationDatabase Compilations(".", std::vector<std::string>());
+  ClangTool Tool(Compilations, {"a.cc"});
+  Tool.mapVirtualFile("a.cc", "int x = 1;");
+  bool FoundX = false;
+  Tool.run(consumeASTs([&](ASTContext &Ctx) {
+             DeclarationName X(&Ctx.Idents.get("x"));
+             FoundX = Ctx.getTranslationUnitDecl()->lookup(X).isSingleResult();
+           }).get());
+  EXPECT_TRUE(FoundX);
+}
+
 struct SkipBodyConsumer : public clang::ASTConsumer {
   /// Skip the 'skipMe' function.
   bool shouldSkipFunctionBody(Decl *D) override {
Index: clang/unittests/Tooling/ExecutionTest.cpp
===================================================================
--- clang/unittests/Tooling/ExecutionTest.cpp
+++ clang/unittests/Tooling/ExecutionTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/Tooling/Execution.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclarationName.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/FrontendAction.h"
@@ -18,10 +19,12 @@
 #include "clang/Tooling/StandaloneExecution.h"
 #include "clang/Tooling/ToolExecutorPluginRegistry.h"
 #include "clang/Tooling/Tooling.h"
+#include "llvm/Testing/Support/SupportHelpers.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <algorithm>
 #include <string>
+#include <vector>
 
 namespace clang {
 namespace tooling {
@@ -97,9 +100,7 @@
 
   StringRef getExecutorName() const override { return ExecutorName; }
 
-  llvm::Error
-  execute(llvm::ArrayRef<std::pair<std::unique_ptr<FrontendActionFactory>,
-                                   ArgumentsAdjuster>>) override {
+  llvm::Error execute(llvm::ArrayRef<Action>) override {
     return llvm::Error::success();
   }
 
@@ -181,14 +182,32 @@
   EXPECT_EQ(Executor->get()->getExecutorName(), TestToolExecutor::ExecutorName);
 }
 
+TEST(CreateToolExecutorTest, ExecuteFromCommandLine) {
+  llvm::unittest::TempFile Source1("test1", ".cpp", "int x = 0;", true);
+  llvm::unittest::TempFile Source2("test2", ".cpp", "int y = 0;", true);
+  std::string Path1 = Source1.path().str();
+  std::string Path2 = Source2.path().str();
+  std::vector<const char *> argv = {"prog", Path1.c_str(), Path2.c_str()};
+
+  int argc = argv.size();
+  int YCount = 0;
+  int Result = executeFromCommandLineArgs(
+      argc, &argv[0], consumeASTs([&](ASTContext &Ctx) {
+        DeclarationName Y(&Ctx.Idents.get("y"));
+        YCount += Ctx.getTranslationUnitDecl()->lookup(Y).isSingleResult();
+      }));
+  EXPECT_EQ(Result, 0);
+  EXPECT_EQ(YCount, 1);
+}
+
 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(newFrontendActionFactory<SyntaxOnlyAction>(),
-                              getClangSyntaxOnlyAdjuster());
+  auto Err = Executor.execute({newFrontendActionFactory<SyntaxOnlyAction>(),
+                               getClangSyntaxOnlyAdjuster()});
   ASSERT_TRUE(!Err);
 }
 
Index: clang/lib/Tooling/Tooling.cpp
===================================================================
--- clang/lib/Tooling/Tooling.cpp
+++ clang/lib/Tooling/Tooling.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Tooling/Tooling.h"
+#include "clang/AST/ASTConsumer.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Basic/DiagnosticOptions.h"
@@ -27,6 +28,7 @@
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendAction.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/FrontendOptions.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
@@ -35,7 +37,9 @@
 #include "clang/Tooling/ArgumentsAdjusters.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/FunctionExtras.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
@@ -709,5 +713,31 @@
   return std::move(ASTs[0]);
 }
 
+std::unique_ptr<FrontendActionFactory>
+consumeASTs(llvm::unique_function<void(ASTContext &) const> Func) {
+  using Delegate = llvm::unique_function<void(ASTContext &) const>;
+  struct Consumer : public ASTConsumer {
+    const Delegate &Func;
+    Consumer(const Delegate &Func) : Func(Func) {}
+    void HandleTranslationUnit(ASTContext &Ctx) override { Func(Ctx); }
+  };
+  struct Action : public ASTFrontendAction {
+    const Delegate &Func;
+    Action(const Delegate &Func) : Func(Func) {}
+    std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &,
+                                                   StringRef) override {
+      return std::make_unique<Consumer>(Func);
+    }
+  };
+  struct Factory : public FrontendActionFactory {
+    Delegate Func;
+    Factory(Delegate Func) : Func(std::move(Func)) {}
+    std::unique_ptr<FrontendAction> create() override {
+      return std::make_unique<Action>(Func);
+    }
+  };
+  return std::make_unique<Factory>(std::move(Func));
+}
+
 } // namespace tooling
 } // namespace clang
Index: clang/lib/Tooling/StandaloneExecution.cpp
===================================================================
--- clang/lib/Tooling/StandaloneExecution.cpp
+++ clang/lib/Tooling/StandaloneExecution.cpp
@@ -49,10 +49,7 @@
   Tool.clearArgumentsAdjusters();
 }
 
-llvm::Error StandaloneToolExecutor::execute(
-    llvm::ArrayRef<
-        std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
-        Actions) {
+llvm::Error StandaloneToolExecutor::execute(llvm::ArrayRef<Action> Actions) {
   if (Actions.empty())
     return make_string_error("No action to execute.");
 
@@ -61,9 +58,9 @@
         "Only support executing exactly 1 action at this point.");
 
   auto &Action = Actions.front();
-  Tool.appendArgumentsAdjuster(Action.second);
+  Tool.appendArgumentsAdjuster(Action.Adjuster);
   Tool.appendArgumentsAdjuster(ArgsAdjuster);
-  if (Tool.run(Action.first.get()))
+  if (Tool.run(Action.Factory.get()))
     return make_string_error("Failed to run action.");
 
   return llvm::Error::success();
Index: clang/lib/Tooling/Execution.cpp
===================================================================
--- clang/lib/Tooling/Execution.cpp
+++ clang/lib/Tooling/Execution.cpp
@@ -9,6 +9,10 @@
 #include "clang/Tooling/Execution.h"
 #include "clang/Tooling/ToolExecutorPluginRegistry.h"
 #include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#include <utility>
 
 LLVM_INSTANTIATE_REGISTRY(clang::tooling::ToolExecutorPluginRegistry)
 
@@ -39,19 +43,7 @@
   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::Error ToolExecutor::execute(Action A) { return execute(ArrayRef(A)); }
 
 namespace internal {
 llvm::Expected<std::unique_ptr<ToolExecutor>>
@@ -92,6 +84,23 @@
                                                          Overview);
 }
 
+int executeFromCommandLineArgs(int &argc, const char **argv,
+                               ToolExecutor::Action Action,
+                               const char *Overview) {
+  static llvm::cl::OptionCategory Category("Execution options");
+  auto Exec = createExecutorFromCommandLineArgs(argc, argv, Category, Overview);
+  if (!Exec) {
+    llvm::errs() << toString(Exec.takeError());
+    return 1;
+  }
+  auto Err = (*Exec)->execute(std::move(Action));
+  if (Err) {
+    llvm::errs() << toString(std::move(Err));
+    return 2;
+  }
+  return 0;
+}
+
 // This anchor is used to force the linker to link in the generated object file
 // and thus register the StandaloneToolExecutorPlugin etc.
 extern volatile int StandaloneToolExecutorAnchorSource;
Index: clang/lib/Tooling/AllTUsExecution.cpp
===================================================================
--- clang/lib/Tooling/AllTUsExecution.cpp
+++ clang/lib/Tooling/AllTUsExecution.cpp
@@ -75,10 +75,7 @@
       Results(new ThreadSafeToolResults), Context(Results.get()),
       ThreadCount(ThreadCount) {}
 
-llvm::Error AllTUsToolExecutor::execute(
-    llvm::ArrayRef<
-        std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
-        Actions) {
+llvm::Error AllTUsToolExecutor::execute(llvm::ArrayRef<Action> Actions) {
   if (Actions.empty())
     return make_string_error("No action to execute.");
 
@@ -127,12 +124,12 @@
                 llvm::vfs::createPhysicalFileSystem();
             ClangTool Tool(Compilations, {Path},
                            std::make_shared<PCHContainerOperations>(), FS);
-            Tool.appendArgumentsAdjuster(Action.second);
+            Tool.appendArgumentsAdjuster(Action.Adjuster);
             Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters());
             for (const auto &FileAndContent : OverlayFiles)
               Tool.mapVirtualFile(FileAndContent.first(),
                                   FileAndContent.second);
-            if (Tool.run(Action.first.get()))
+            if (Tool.run(Action.Factory.get()))
               AppendError(llvm::Twine("Failed to run action on ") + Path +
                           "\n");
           },
Index: clang/include/clang/Tooling/Tooling.h
===================================================================
--- clang/include/clang/Tooling/Tooling.h
+++ clang/include/clang/Tooling/Tooling.h
@@ -36,6 +36,7 @@
 #include "clang/Frontend/PCHContainerOperations.h"
 #include "clang/Tooling/ArgumentsAdjusters.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/FunctionExtras.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
@@ -122,6 +123,18 @@
                                     std::is_base_of_v<ASTConsumer, T>>>
 std::unique_ptr<FrontendActionFactory> newFrontendActionFactory();
 
+/// Allows a function that accepts ASTs to be used as a FrontendActionFactory.
+///
+/// Example:
+///   Executor->execute(consumeASTs([&](ASTContext &Ctx) {
+///      Ctx.getTranslationUnitDecl()->dump();
+///   }));
+///
+/// This is suitable for actions that merely consume the AST that clang builds.
+/// It does not provide a way to customize the preprocessor etc.
+std::unique_ptr<FrontendActionFactory>
+    consumeASTs(llvm::unique_function<void(ASTContext &) const>);
+
 /// Callbacks called before and after each source file processed by a
 /// FrontendAction created by the FrontedActionFactory returned by \c
 /// newFrontendActionFactory.
Index: clang/include/clang/Tooling/StandaloneExecution.h
===================================================================
--- clang/include/clang/Tooling/StandaloneExecution.h
+++ clang/include/clang/Tooling/StandaloneExecution.h
@@ -55,10 +55,7 @@
 
   using ToolExecutor::execute;
 
-  llvm::Error
-  execute(llvm::ArrayRef<
-          std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
-              Actions) override;
+  llvm::Error execute(llvm::ArrayRef<Action> Actions) override;
 
   /// Set a \c DiagnosticConsumer to use during parsing.
   void setDiagnosticConsumer(DiagnosticConsumer *DiagConsumer) {
Index: clang/include/clang/Tooling/Execution.h
===================================================================
--- clang/include/clang/Tooling/Execution.h
+++ clang/include/clang/Tooling/Execution.h
@@ -115,17 +115,26 @@
   /// Returns the name of a specific executor.
   virtual StringRef getExecutorName() const = 0;
 
+  /// A frontend action to be executed, with optional ArgumentsAdjuster.
+  struct Action {
+    Action(std::unique_ptr<FrontendActionFactory> Factory)
+        : Factory(std::move(Factory)) {}
+    Action(std::unique_ptr<FrontendActionFactory> Factory,
+           ArgumentsAdjuster Adjuster)
+        : Factory(std::move(Factory)), Adjuster(std::move(Adjuster)) {}
+    Action(std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>
+               Pair)
+        : Action(std::move(Pair.first), std::move(Pair.second)) {}
+
+    std::unique_ptr<FrontendActionFactory> Factory;
+    ArgumentsAdjuster Adjuster;
+  };
   /// Executes each action with a corresponding arguments adjuster.
-  virtual llvm::Error
-  execute(llvm::ArrayRef<
-          std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
-              Actions) = 0;
+  /// FIXME: the ability to execute multiple actions appears unused, remove it?
+  virtual llvm::Error execute(llvm::ArrayRef<Action> Actions) = 0;
 
   /// Convenient functions for the above `execute`.
-  llvm::Error execute(std::unique_ptr<FrontendActionFactory> Action);
-  /// Executes an action with an argument adjuster.
-  llvm::Error execute(std::unique_ptr<FrontendActionFactory> Action,
-                      ArgumentsAdjuster Adjuster);
+  llvm::Error execute(Action);
 
   /// Returns a reference to the execution context.
   ///
@@ -178,8 +187,24 @@
 createExecutorFromCommandLineArgsImpl(int &argc, const char **argv,
                                       llvm::cl::OptionCategory &Category,
                                       const char *Overview = nullptr);
+
 } // end namespace internal
 
+/// Runs an action on inputs specified by the command-line arguments.
+///
+/// If errors occur, they are reported to llvm::errs() and nonzero is returned:
+/// - 1 indicates bad tooling flags (failed to initialize executor)
+/// - 2 indicates parsing errors on some files
+///
+/// Expected usage is from main():
+///   int main(int argc, const char **argv) {
+///     return executeFromCommandLineArgs(argc, argv,
+///       consumeASTs([](ASTContext &C){ C.getTranslationUnitDecl()->dump(); });
+///   }
+int executeFromCommandLineArgs(int &argc, const char **argv,
+                               ToolExecutor::Action,
+                               const char *Overview = nullptr);
+
 } // end namespace tooling
 } // end namespace clang
 
Index: clang/include/clang/Tooling/AllTUsExecution.h
===================================================================
--- clang/include/clang/Tooling/AllTUsExecution.h
+++ clang/include/clang/Tooling/AllTUsExecution.h
@@ -47,10 +47,7 @@
 
   using ToolExecutor::execute;
 
-  llvm::Error
-  execute(llvm::ArrayRef<
-          std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
-              Actions) override;
+  llvm::Error execute(llvm::ArrayRef<Action> Actions) override;
 
   ExecutionContext *getExecutionContext() override { return &Context; };
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to