Index: clang/include/clang/Frontend/FrontendAction.h =================================================================== --- clang/include/clang/Frontend/FrontendAction.h +++ clang/include/clang/Frontend/FrontendAction.h @@ -296,33 +296,51 @@ }; /// A frontend action which simply wraps some other runtime-specified -/// frontend action. +/// frontend actions. +/// +/// Owns a list of FrontendAction-s and implements FrontendAction interface by +/// forwarding to actions in the list (in the order they were provided) - +/// with the exceptions of: +/// - ExecuteAction(): First action's method is called only. +/// - getTranslationUnitKind: Result of the first action's method is returned. +/// +/// For methods returning bool we return boolean product of either particular +/// values or their negation depending on the semantics. +/// We don't exit early if some of the methods returns false as those +/// could have side effects. /// /// Deriving from this class allows an action to inject custom logic around -/// some existing action's behavior. It implements every virtual method in -/// the FrontendAction interface by forwarding to the wrapped action. +/// some existing action's behavior. class WrapperFrontendAction : public FrontendAction { - std::unique_ptr WrappedAction; + std::vector> Actions; protected: bool PrepareToExecuteAction(CompilerInstance &CI) override; + /// \r consumer that forwards to consumers produced by actions in the list. std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override; bool BeginInvocation(CompilerInstance &CI) override; bool BeginSourceFileAction(CompilerInstance &CI) override; + /// Invokes ExecuteAction of the first action in the list. void ExecuteAction() override; void EndSourceFileAction() override; public: - /// Construct a WrapperFrontendAction from an existing action, taking - /// ownership of it. - WrapperFrontendAction(std::unique_ptr WrappedAction); + WrapperFrontendAction(std::initializer_list ActionList); + WrapperFrontendAction(std::unique_ptr Action); + /// Does ANY of the actions use preprocessor only? bool usesPreprocessorOnly() const override; + /// \r getTranslationUnitKind() of the first action or TU_Complete if there + /// are no actions. TranslationUnitKind getTranslationUnitKind() override; + /// Do ALL the actions support use with PCH? bool hasPCHSupport() const override; + /// Do ALL the actions support use with AST files? bool hasASTFileSupport() const override; + /// Do ALL the actions support use with IR file? bool hasIRSupport() const override; + /// Do ALL the actions support use with code completion? bool hasCodeCompletionSupport() const override; }; Index: clang/lib/Frontend/FrontendAction.cpp =================================================================== --- clang/lib/Frontend/FrontendAction.cpp +++ clang/lib/Frontend/FrontendAction.cpp @@ -1051,51 +1051,94 @@ } bool WrapperFrontendAction::PrepareToExecuteAction(CompilerInstance &CI) { - return WrappedAction->PrepareToExecuteAction(CI); + bool Result = true; + for (auto &Action : Actions) + Result &= Action->PrepareToExecuteAction(CI); + return Result; } std::unique_ptr WrapperFrontendAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - return WrappedAction->CreateASTConsumer(CI, InFile); + std::vector> Consumers; + for (auto &Action : Actions) { + if (!Action->usesPreprocessorOnly()) + Consumers.push_back(Action->CreateASTConsumer(CI, InFile)); + } + + return llvm::make_unique(std::move(Consumers)); } bool WrapperFrontendAction::BeginInvocation(CompilerInstance &CI) { - return WrappedAction->BeginInvocation(CI); + bool Result = true; + for (auto &Action : Actions) + Result &= Action->BeginInvocation(CI); + return Result; } bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI) { - WrappedAction->setCurrentInput(getCurrentInput()); - WrappedAction->setCompilerInstance(&CI); - auto Ret = WrappedAction->BeginSourceFileAction(CI); + auto CurrentInput = getCurrentInput(); + bool Result = true; + for (auto &Action : Actions) { + Action->setCurrentInput(getCurrentInput()); + Action->setCompilerInstance(&CI); + Result &= Action->BeginSourceFileAction(CI); + } // BeginSourceFileAction may change CurrentInput, e.g. during module builds. - setCurrentInput(WrappedAction->getCurrentInput()); - return Ret; + setCurrentInput(CurrentInput); + return Result; } void WrapperFrontendAction::ExecuteAction() { - WrappedAction->ExecuteAction(); + if (Actions.size() > 0) + return Actions.front()->ExecuteAction(); } void WrapperFrontendAction::EndSourceFileAction() { - WrappedAction->EndSourceFileAction(); + for (auto &Action : Actions) + Action->EndSourceFileAction(); } bool WrapperFrontendAction::usesPreprocessorOnly() const { - return WrappedAction->usesPreprocessorOnly(); + // The default is that we don't assume anything special. + bool Result = false; + for (auto &Action : Actions) + Result |= Action->usesPreprocessorOnly(); + return Result; } TranslationUnitKind WrapperFrontendAction::getTranslationUnitKind() { - return WrappedAction->getTranslationUnitKind(); + if (Actions.size() > 0) + return Actions[0]->getTranslationUnitKind(); + return TU_Complete; } bool WrapperFrontendAction::hasPCHSupport() const { - return WrappedAction->hasPCHSupport(); + bool Result = true; + for (auto &Action : Actions) + Result &= Action->hasPCHSupport(); + return Result; } bool WrapperFrontendAction::hasASTFileSupport() const { - return WrappedAction->hasASTFileSupport(); + bool Result = true; + for (auto &Action : Actions) + Result &= Action->hasASTFileSupport(); + return Result; } bool WrapperFrontendAction::hasIRSupport() const { - return WrappedAction->hasIRSupport(); + bool Result = true; + for (auto &Action : Actions) + Result &= Action->hasIRSupport(); + return Result; } bool WrapperFrontendAction::hasCodeCompletionSupport() const { - return WrappedAction->hasCodeCompletionSupport(); + bool Result = true; + for (auto &Action : Actions) + Result &= Action->hasCodeCompletionSupport(); + return Result; } WrapperFrontendAction::WrapperFrontendAction( - std::unique_ptr WrappedAction) - : WrappedAction(std::move(WrappedAction)) {} + std::initializer_list ActionList) { + Actions.reserve(ActionList.size()); + for (FrontendAction *Action : ActionList) + Actions.emplace_back(Action); +} +WrapperFrontendAction::WrapperFrontendAction( + std::unique_ptr Action) { + Actions.emplace_back(std::move(Action)); +} Index: clang/unittests/Frontend/CMakeLists.txt =================================================================== --- clang/unittests/Frontend/CMakeLists.txt +++ clang/unittests/Frontend/CMakeLists.txt @@ -11,6 +11,7 @@ ParsedSourceLocationTest.cpp PCHPreambleTest.cpp OutputStreamTest.cpp + WrapperFrontendActionTest.cpp ) clang_target_link_libraries(FrontendTests PRIVATE Index: clang/unittests/Frontend/WrapperFrontendActionTest.cpp =================================================================== --- /dev/null +++ clang/unittests/Frontend/WrapperFrontendActionTest.cpp @@ -0,0 +1,162 @@ +//===- unittests/Frontend/WrapperFrontendActionTest.cpp -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Sema/Sema.h" +#include "clang/Serialization/InMemoryModuleCache.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ToolOutputFile.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; +using namespace clang; + +namespace { + +class LoggingASTConsumer : public ASTConsumer { +public: + LoggingASTConsumer(const std::string &ID, std::string &Log) + : ID(ID), Log(Log) {} + + const std::string &ID; + std::string &Log; + + void HandleTranslationUnit(ASTContext &context) override { + Log += "LoggingASTConsumer-" + ID + "-HandleTranslationUnit"; + } +}; + +class LoggingASTFrontendAction : public ASTFrontendAction { +public: + LoggingASTFrontendAction(const std::string &ID, std::string &Log) + : ID(ID), Log(Log) {} + + const std::string &ID; + std::string &Log; + + bool BeginSourceFileAction(CompilerInstance &CI) override { + setCompilerInstance(&CI); + CI.resetAndLeakPreprocessor(); + Log += "LoggingASTFrontendAction-" + ID + "-BeginSourceFileAction"; + return ASTFrontendAction::BeginSourceFileAction(CI); + } + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return llvm::make_unique(ID, Log); + } +}; + +class TestASTFrontendAction : public ASTFrontendAction { +public: + TestASTFrontendAction(bool enableIncrementalProcessing = false, + bool actOnEndOfTranslationUnit = false) + : EnableIncrementalProcessing(enableIncrementalProcessing), + ActOnEndOfTranslationUnit(actOnEndOfTranslationUnit) {} + + bool EnableIncrementalProcessing; + bool ActOnEndOfTranslationUnit; + std::vector decl_names; + + bool BeginSourceFileAction(CompilerInstance &ci) override { + if (EnableIncrementalProcessing) + ci.getPreprocessor().enableIncrementalProcessing(); + + return ASTFrontendAction::BeginSourceFileAction(ci); + } + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return llvm::make_unique(CI, ActOnEndOfTranslationUnit, + decl_names); + } + +private: + class Visitor : public ASTConsumer, public RecursiveASTVisitor { + public: + Visitor(CompilerInstance &CI, bool ActOnEndOfTranslationUnit, + std::vector &decl_names) + : CI(CI), ActOnEndOfTranslationUnit(ActOnEndOfTranslationUnit), + decl_names_(decl_names) {} + + void HandleTranslationUnit(ASTContext &context) override { + if (ActOnEndOfTranslationUnit) { + CI.getSema().ActOnEndOfTranslationUnit(); + } + TraverseDecl(context.getTranslationUnitDecl()); + } + + virtual bool VisitNamedDecl(NamedDecl *Decl) { + decl_names_.push_back(Decl->getQualifiedNameAsString()); + return true; + } + + private: + CompilerInstance &CI; + bool ActOnEndOfTranslationUnit; + std::vector &decl_names_; + }; +}; + +TEST(WrapperFrontendActionTest, Trivial) { + auto invocation = std::make_shared(); + invocation->getPreprocessorOpts().addRemappedFile( + "test.cc", + MemoryBuffer::getMemBuffer("int main() { float x; }").release()); + invocation->getFrontendOpts().Inputs.push_back( + FrontendInputFile("test.cc", InputKind::CXX)); + invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly; + invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu"; + CompilerInstance compiler; + compiler.setInvocation(std::move(invocation)); + compiler.createDiagnostics(); + + std::string ActionLog; + LoggingASTFrontendAction Action("A", ActionLog); + + ASSERT_TRUE(compiler.ExecuteAction(Action)); +} + +TEST(WrapperFrontendActionTest, Order) { + auto invocation = std::make_shared(); + invocation->getPreprocessorOpts().addRemappedFile( + "test.cc", + MemoryBuffer::getMemBuffer("int main() { float x; }").release()); + invocation->getFrontendOpts().Inputs.push_back( + FrontendInputFile("test.cc", InputKind::CXX)); + invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly; + invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu"; + CompilerInstance compiler; + compiler.setInvocation(std::move(invocation)); + compiler.createDiagnostics(); + + std::string ActionLog; + WrapperFrontendAction Action({new LoggingASTFrontendAction("A", ActionLog), + new LoggingASTFrontendAction("B", ActionLog), + new LoggingASTFrontendAction("C", ActionLog)}); + + ASSERT_TRUE(compiler.ExecuteAction(Action)); + EXPECT_EQ(ActionLog, "LoggingASTFrontendAction-A-BeginSourceFileAction" + "LoggingASTFrontendAction-B-BeginSourceFileAction" + "LoggingASTFrontendAction-C-BeginSourceFileAction" + "LoggingASTConsumer-A-HandleTranslationUnit" + "LoggingASTConsumer-B-HandleTranslationUnit" + "LoggingASTConsumer-C-HandleTranslationUnit"); +} + +} // anonymous namespace