diff --git a/clang/include/clang/Tooling/AllTUsExecution.h b/clang/include/clang/Tooling/AllTUsExecution.h --- a/clang/include/clang/Tooling/AllTUsExecution.h +++ b/clang/include/clang/Tooling/AllTUsExecution.h @@ -47,10 +47,7 @@ using ToolExecutor::execute; - llvm::Error - execute(llvm::ArrayRef< - std::pair, ArgumentsAdjuster>> - Actions) override; + llvm::Error execute(llvm::ArrayRef Actions) override; ExecutionContext *getExecutionContext() override { return &Context; }; diff --git a/clang/include/clang/Tooling/Execution.h b/clang/include/clang/Tooling/Execution.h --- a/clang/include/clang/Tooling/Execution.h +++ b/clang/include/clang/Tooling/Execution.h @@ -115,17 +115,23 @@ /// 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 Factory) + : Factory(std::move(Factory)) {} + Action(std::unique_ptr Factory, + ArgumentsAdjuster Adjuster) + : Factory(std::move(Factory)), Adjuster(std::move(Adjuster)) {} + + std::unique_ptr Factory; + ArgumentsAdjuster Adjuster; + }; /// Executes each action with a corresponding arguments adjuster. - virtual llvm::Error - execute(llvm::ArrayRef< - std::pair, ArgumentsAdjuster>> - Actions) = 0; + /// FIXME: the ability to execute multiple actions appears unused, remove it? + virtual llvm::Error execute(llvm::ArrayRef Actions) = 0; /// Convenient functions for the above `execute`. - llvm::Error execute(std::unique_ptr Action); - /// Executes an action with an argument adjuster. - llvm::Error execute(std::unique_ptr Action, - ArgumentsAdjuster Adjuster); + llvm::Error execute(Action); /// Returns a reference to the execution context. /// @@ -178,8 +184,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 diff --git a/clang/include/clang/Tooling/StandaloneExecution.h b/clang/include/clang/Tooling/StandaloneExecution.h --- a/clang/include/clang/Tooling/StandaloneExecution.h +++ b/clang/include/clang/Tooling/StandaloneExecution.h @@ -55,10 +55,7 @@ using ToolExecutor::execute; - llvm::Error - execute(llvm::ArrayRef< - std::pair, ArgumentsAdjuster>> - Actions) override; + llvm::Error execute(llvm::ArrayRef Actions) override; /// Set a \c DiagnosticConsumer to use during parsing. void setDiagnosticConsumer(DiagnosticConsumer *DiagConsumer) { diff --git a/clang/include/clang/Tooling/Tooling.h b/clang/include/clang/Tooling/Tooling.h --- a/clang/include/clang/Tooling/Tooling.h +++ b/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" @@ -45,6 +46,7 @@ #include "llvm/Support/VirtualFileSystem.h" #include #include +#include #include #include @@ -111,14 +113,28 @@ /// Returns a new FrontendActionFactory for a given type. /// -/// T must derive from clang::FrontendAction. +/// T must derive from clang::FrontendAction or clang::ASTConsumer. /// /// Example: /// std::unique_ptr Factory = /// newFrontendActionFactory(); -template +template || + std::is_base_of_v>> std::unique_ptr 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 + consumeASTs(llvm::unique_function); + /// Callbacks called before and after each source file processed by a /// FrontendAction created by the FrontedActionFactory returned by \c /// newFrontendActionFactory. @@ -393,17 +409,27 @@ bool PrintErrorMessage = true; }; -template +template std::unique_ptr newFrontendActionFactory() { - class SimpleFrontendActionFactory : public FrontendActionFactory { - public: - std::unique_ptr create() override { - return std::make_unique(); - } - }; + if constexpr (std::is_base_of_v) { + class SimpleFrontendActionFactory : public FrontendActionFactory { + public: + std::unique_ptr create() override { + return std::make_unique(); + } + }; - return std::unique_ptr( - new SimpleFrontendActionFactory); + return std::unique_ptr( + new SimpleFrontendActionFactory); + } else { + class SimpleFrontendAction : public ASTFrontendAction { + std::unique_ptr CreateASTConsumer(CompilerInstance &, + StringRef) override { + return std::make_unique(); + } + }; + return newFrontendActionFactory(); + } } template diff --git a/clang/lib/Tooling/AllTUsExecution.cpp b/clang/lib/Tooling/AllTUsExecution.cpp --- a/clang/lib/Tooling/AllTUsExecution.cpp +++ b/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, ArgumentsAdjuster>> - Actions) { +llvm::Error AllTUsToolExecutor::execute(llvm::ArrayRef 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(), 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"); }, diff --git a/clang/lib/Tooling/Execution.cpp b/clang/lib/Tooling/Execution.cpp --- a/clang/lib/Tooling/Execution.cpp +++ b/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 LLVM_INSTANTIATE_REGISTRY(clang::tooling::ToolExecutorPluginRegistry) @@ -39,19 +43,7 @@ Results->addResult(Key, Value); } -llvm::Error -ToolExecutor::execute(std::unique_ptr Action) { - return execute(std::move(Action), ArgumentsAdjuster()); -} - -llvm::Error ToolExecutor::execute(std::unique_ptr Action, - ArgumentsAdjuster Adjuster) { - std::vector< - std::pair, 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> @@ -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; diff --git a/clang/lib/Tooling/StandaloneExecution.cpp b/clang/lib/Tooling/StandaloneExecution.cpp --- a/clang/lib/Tooling/StandaloneExecution.cpp +++ b/clang/lib/Tooling/StandaloneExecution.cpp @@ -49,10 +49,7 @@ Tool.clearArgumentsAdjusters(); } -llvm::Error StandaloneToolExecutor::execute( - llvm::ArrayRef< - std::pair, ArgumentsAdjuster>> - Actions) { +llvm::Error StandaloneToolExecutor::execute(llvm::ArrayRef 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(); diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp --- a/clang/lib/Tooling/Tooling.cpp +++ b/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 +consumeASTs(llvm::unique_function Func) { + using Delegate = llvm::unique_function; + 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 CreateASTConsumer(CompilerInstance &, + StringRef) override { + return std::make_unique(Func); + } + }; + struct Factory : public FrontendActionFactory { + Delegate Func; + Factory(Delegate Func) : Func(std::move(Func)) {} + std::unique_ptr create() override { + return std::make_unique(Func); + } + }; + return std::make_unique(std::move(Func)); +} + } // namespace tooling } // namespace clang diff --git a/clang/unittests/Tooling/ExecutionTest.cpp b/clang/unittests/Tooling/ExecutionTest.cpp --- a/clang/unittests/Tooling/ExecutionTest.cpp +++ b/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 #include +#include namespace clang { namespace tooling { @@ -97,9 +100,7 @@ StringRef getExecutorName() const override { return ExecutorName; } - llvm::Error - execute(llvm::ArrayRef, - ArgumentsAdjuster>>) override { + llvm::Error execute(llvm::ArrayRef) 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 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()); StandaloneToolExecutor Executor(Compilations, std::vector(1, "a.cc")); Executor.mapVirtualFile("a.cc", "int x = 0;"); - auto Err = Executor.execute(newFrontendActionFactory(), - getClangSyntaxOnlyAdjuster()); + auto Err = Executor.execute({newFrontendActionFactory(), + getClangSyntaxOnlyAdjuster()}); ASSERT_TRUE(!Err); } diff --git a/clang/unittests/Tooling/ToolingTest.cpp b/clang/unittests/Tooling/ToolingTest.cpp --- a/clang/unittests/Tooling/ToolingTest.cpp +++ b/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" @@ -152,13 +153,21 @@ EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); } -TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { +TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromActionType) { std::unique_ptr Factory( newFrontendActionFactory()); std::unique_ptr Action(Factory->create()); EXPECT_TRUE(Action.get() != nullptr); } +TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromConsumerType) { + class MyASTConsumer : public ASTConsumer {}; + std::unique_ptr Factory( + newFrontendActionFactory()); + std::unique_ptr Action(Factory->create()); + EXPECT_TRUE(Action.get() != nullptr); +} + struct IndependentFrontendActionCreator { std::unique_ptr newASTConsumer() { return std::make_unique(nullptr); @@ -503,6 +512,18 @@ } #endif +TEST(newFrontendActionFactory, ConsumeASTs) { + FixedCompilationDatabase Compilations(".", std::vector()); + 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 {