clang API Documentation

FrontendAction.cpp
Go to the documentation of this file.
00001 //===--- FrontendAction.cpp -----------------------------------------------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 
00010 #include "clang/Frontend/FrontendAction.h"
00011 #include "clang/AST/ASTConsumer.h"
00012 #include "clang/AST/ASTContext.h"
00013 #include "clang/AST/DeclGroup.h"
00014 #include "clang/Frontend/ASTUnit.h"
00015 #include "clang/Frontend/CompilerInstance.h"
00016 #include "clang/Frontend/FrontendDiagnostic.h"
00017 #include "clang/Frontend/FrontendPluginRegistry.h"
00018 #include "clang/Frontend/LayoutOverrideSource.h"
00019 #include "clang/Frontend/MultiplexConsumer.h"
00020 #include "clang/Frontend/Utils.h"
00021 #include "clang/Lex/HeaderSearch.h"
00022 #include "clang/Lex/Preprocessor.h"
00023 #include "clang/Parse/ParseAST.h"
00024 #include "clang/Serialization/ASTDeserializationListener.h"
00025 #include "clang/Serialization/ASTReader.h"
00026 #include "clang/Serialization/GlobalModuleIndex.h"
00027 #include "llvm/Support/ErrorHandling.h"
00028 #include "llvm/Support/FileSystem.h"
00029 #include "llvm/Support/MemoryBuffer.h"
00030 #include "llvm/Support/Timer.h"
00031 #include "llvm/Support/raw_ostream.h"
00032 #include <system_error>
00033 using namespace clang;
00034 
00035 template class llvm::Registry<clang::PluginASTAction>;
00036 
00037 namespace {
00038 
00039 class DelegatingDeserializationListener : public ASTDeserializationListener {
00040   ASTDeserializationListener *Previous;
00041   bool DeletePrevious;
00042 
00043 public:
00044   explicit DelegatingDeserializationListener(
00045       ASTDeserializationListener *Previous, bool DeletePrevious)
00046       : Previous(Previous), DeletePrevious(DeletePrevious) {}
00047   virtual ~DelegatingDeserializationListener() {
00048     if (DeletePrevious)
00049       delete Previous;
00050   }
00051 
00052   void ReaderInitialized(ASTReader *Reader) override {
00053     if (Previous)
00054       Previous->ReaderInitialized(Reader);
00055   }
00056   void IdentifierRead(serialization::IdentID ID,
00057                       IdentifierInfo *II) override {
00058     if (Previous)
00059       Previous->IdentifierRead(ID, II);
00060   }
00061   void TypeRead(serialization::TypeIdx Idx, QualType T) override {
00062     if (Previous)
00063       Previous->TypeRead(Idx, T);
00064   }
00065   void DeclRead(serialization::DeclID ID, const Decl *D) override {
00066     if (Previous)
00067       Previous->DeclRead(ID, D);
00068   }
00069   void SelectorRead(serialization::SelectorID ID, Selector Sel) override {
00070     if (Previous)
00071       Previous->SelectorRead(ID, Sel);
00072   }
00073   void MacroDefinitionRead(serialization::PreprocessedEntityID PPID,
00074                            MacroDefinition *MD) override {
00075     if (Previous)
00076       Previous->MacroDefinitionRead(PPID, MD);
00077   }
00078 };
00079 
00080 /// \brief Dumps deserialized declarations.
00081 class DeserializedDeclsDumper : public DelegatingDeserializationListener {
00082 public:
00083   explicit DeserializedDeclsDumper(ASTDeserializationListener *Previous,
00084                                    bool DeletePrevious)
00085       : DelegatingDeserializationListener(Previous, DeletePrevious) {}
00086 
00087   void DeclRead(serialization::DeclID ID, const Decl *D) override {
00088     llvm::outs() << "PCH DECL: " << D->getDeclKindName();
00089     if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
00090       llvm::outs() << " - " << *ND;
00091     llvm::outs() << "\n";
00092 
00093     DelegatingDeserializationListener::DeclRead(ID, D);
00094   }
00095 };
00096 
00097 /// \brief Checks deserialized declarations and emits error if a name
00098 /// matches one given in command-line using -error-on-deserialized-decl.
00099 class DeserializedDeclsChecker : public DelegatingDeserializationListener {
00100   ASTContext &Ctx;
00101   std::set<std::string> NamesToCheck;
00102 
00103 public:
00104   DeserializedDeclsChecker(ASTContext &Ctx,
00105                            const std::set<std::string> &NamesToCheck,
00106                            ASTDeserializationListener *Previous,
00107                            bool DeletePrevious)
00108       : DelegatingDeserializationListener(Previous, DeletePrevious), Ctx(Ctx),
00109         NamesToCheck(NamesToCheck) {}
00110 
00111   void DeclRead(serialization::DeclID ID, const Decl *D) override {
00112     if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
00113       if (NamesToCheck.find(ND->getNameAsString()) != NamesToCheck.end()) {
00114         unsigned DiagID
00115           = Ctx.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error,
00116                                                  "%0 was deserialized");
00117         Ctx.getDiagnostics().Report(Ctx.getFullLoc(D->getLocation()), DiagID)
00118             << ND->getNameAsString();
00119       }
00120 
00121     DelegatingDeserializationListener::DeclRead(ID, D);
00122   }
00123 };
00124 
00125 } // end anonymous namespace
00126 
00127 FrontendAction::FrontendAction() : Instance(nullptr) {}
00128 
00129 FrontendAction::~FrontendAction() {}
00130 
00131 void FrontendAction::setCurrentInput(const FrontendInputFile &CurrentInput,
00132                                      std::unique_ptr<ASTUnit> AST) {
00133   this->CurrentInput = CurrentInput;
00134   CurrentASTUnit = std::move(AST);
00135 }
00136 
00137 std::unique_ptr<ASTConsumer>
00138 FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI,
00139                                          StringRef InFile) {
00140   std::unique_ptr<ASTConsumer> Consumer = CreateASTConsumer(CI, InFile);
00141   if (!Consumer)
00142     return nullptr;
00143 
00144   if (CI.getFrontendOpts().AddPluginActions.size() == 0)
00145     return Consumer;
00146 
00147   // Make sure the non-plugin consumer is first, so that plugins can't
00148   // modifiy the AST.
00149   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
00150   Consumers.push_back(std::move(Consumer));
00151 
00152   for (size_t i = 0, e = CI.getFrontendOpts().AddPluginActions.size();
00153        i != e; ++i) { 
00154     // This is O(|plugins| * |add_plugins|), but since both numbers are
00155     // way below 50 in practice, that's ok.
00156     for (FrontendPluginRegistry::iterator
00157         it = FrontendPluginRegistry::begin(),
00158         ie = FrontendPluginRegistry::end();
00159         it != ie; ++it) {
00160       if (it->getName() != CI.getFrontendOpts().AddPluginActions[i])
00161         continue;
00162       std::unique_ptr<PluginASTAction> P = it->instantiate();
00163       if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i]))
00164         Consumers.push_back(P->CreateASTConsumer(CI, InFile));
00165     }
00166   }
00167 
00168   return llvm::make_unique<MultiplexConsumer>(std::move(Consumers));
00169 }
00170 
00171 bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
00172                                      const FrontendInputFile &Input) {
00173   assert(!Instance && "Already processing a source file!");
00174   assert(!Input.isEmpty() && "Unexpected empty filename!");
00175   setCurrentInput(Input);
00176   setCompilerInstance(&CI);
00177 
00178   StringRef InputFile = Input.getFile();
00179   bool HasBegunSourceFile = false;
00180   if (!BeginInvocation(CI))
00181     goto failure;
00182 
00183   // AST files follow a very different path, since they share objects via the
00184   // AST unit.
00185   if (Input.getKind() == IK_AST) {
00186     assert(!usesPreprocessorOnly() &&
00187            "Attempt to pass AST file to preprocessor only action!");
00188     assert(hasASTFileSupport() &&
00189            "This action does not have AST file support!");
00190 
00191     IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics());
00192 
00193     std::unique_ptr<ASTUnit> AST =
00194         ASTUnit::LoadFromASTFile(InputFile, Diags, CI.getFileSystemOpts());
00195 
00196     if (!AST)
00197       goto failure;
00198 
00199     // Inform the diagnostic client we are processing a source file.
00200     CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), nullptr);
00201     HasBegunSourceFile = true;
00202 
00203     // Set the shared objects, these are reset when we finish processing the
00204     // file, otherwise the CompilerInstance will happily destroy them.
00205     CI.setFileManager(&AST->getFileManager());
00206     CI.setSourceManager(&AST->getSourceManager());
00207     CI.setPreprocessor(&AST->getPreprocessor());
00208     CI.setASTContext(&AST->getASTContext());
00209 
00210     setCurrentInput(Input, std::move(AST));
00211 
00212     // Initialize the action.
00213     if (!BeginSourceFileAction(CI, InputFile))
00214       goto failure;
00215 
00216     // Create the AST consumer.
00217     CI.setASTConsumer(CreateWrappedASTConsumer(CI, InputFile));
00218     if (!CI.hasASTConsumer())
00219       goto failure;
00220 
00221     return true;
00222   }
00223 
00224   if (!CI.hasVirtualFileSystem()) {
00225     if (IntrusiveRefCntPtr<vfs::FileSystem> VFS =
00226           createVFSFromCompilerInvocation(CI.getInvocation(),
00227                                           CI.getDiagnostics()))
00228       CI.setVirtualFileSystem(VFS);
00229     else
00230       goto failure;
00231   }
00232 
00233   // Set up the file and source managers, if needed.
00234   if (!CI.hasFileManager())
00235     CI.createFileManager();
00236   if (!CI.hasSourceManager())
00237     CI.createSourceManager(CI.getFileManager());
00238 
00239   // IR files bypass the rest of initialization.
00240   if (Input.getKind() == IK_LLVM_IR) {
00241     assert(hasIRSupport() &&
00242            "This action does not have IR file support!");
00243 
00244     // Inform the diagnostic client we are processing a source file.
00245     CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), nullptr);
00246     HasBegunSourceFile = true;
00247 
00248     // Initialize the action.
00249     if (!BeginSourceFileAction(CI, InputFile))
00250       goto failure;
00251 
00252     // Initialize the main file entry.
00253     if (!CI.InitializeSourceManager(CurrentInput))
00254       goto failure;
00255 
00256     return true;
00257   }
00258 
00259   // If the implicit PCH include is actually a directory, rather than
00260   // a single file, search for a suitable PCH file in that directory.
00261   if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) {
00262     FileManager &FileMgr = CI.getFileManager();
00263     PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
00264     StringRef PCHInclude = PPOpts.ImplicitPCHInclude;
00265     if (const DirectoryEntry *PCHDir = FileMgr.getDirectory(PCHInclude)) {
00266       std::error_code EC;
00267       SmallString<128> DirNative;
00268       llvm::sys::path::native(PCHDir->getName(), DirNative);
00269       bool Found = false;
00270       for (llvm::sys::fs::directory_iterator Dir(DirNative.str(), EC), DirEnd;
00271            Dir != DirEnd && !EC; Dir.increment(EC)) {
00272         // Check whether this is an acceptable AST file.
00273         if (ASTReader::isAcceptableASTFile(Dir->path(), FileMgr,
00274                                            CI.getLangOpts(),
00275                                            CI.getTargetOpts(),
00276                                            CI.getPreprocessorOpts())) {
00277           PPOpts.ImplicitPCHInclude = Dir->path();
00278           Found = true;
00279           break;
00280         }
00281       }
00282 
00283       if (!Found) {
00284         CI.getDiagnostics().Report(diag::err_fe_no_pch_in_dir) << PCHInclude;
00285         return true;
00286       }
00287     }
00288   }
00289 
00290   // Set up the preprocessor if needed. When parsing model files the
00291   // preprocessor of the original source is reused.
00292   if (!isModelParsingAction())
00293     CI.createPreprocessor(getTranslationUnitKind());
00294 
00295   // Inform the diagnostic client we are processing a source file.
00296   CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(),
00297                                            &CI.getPreprocessor());
00298   HasBegunSourceFile = true;
00299 
00300   // Initialize the action.
00301   if (!BeginSourceFileAction(CI, InputFile))
00302     goto failure;
00303 
00304   // Initialize the main file entry. It is important that this occurs after
00305   // BeginSourceFileAction, which may change CurrentInput during module builds.
00306   if (!CI.InitializeSourceManager(CurrentInput))
00307     goto failure;
00308 
00309   // Create the AST context and consumer unless this is a preprocessor only
00310   // action.
00311   if (!usesPreprocessorOnly()) {
00312     // Parsing a model file should reuse the existing ASTContext.
00313     if (!isModelParsingAction())
00314       CI.createASTContext();
00315 
00316     std::unique_ptr<ASTConsumer> Consumer =
00317         CreateWrappedASTConsumer(CI, InputFile);
00318     if (!Consumer)
00319       goto failure;
00320 
00321     // FIXME: should not overwrite ASTMutationListener when parsing model files?
00322     if (!isModelParsingAction())
00323       CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener());
00324 
00325     if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) {
00326       // Convert headers to PCH and chain them.
00327       IntrusiveRefCntPtr<ExternalSemaSource> source, FinalReader;
00328       source = createChainedIncludesSource(CI, FinalReader);
00329       if (!source)
00330         goto failure;
00331       CI.setModuleManager(static_cast<ASTReader *>(FinalReader.get()));
00332       CI.getASTContext().setExternalSource(source);
00333     } else if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) {
00334       // Use PCH.
00335       assert(hasPCHSupport() && "This action does not have PCH support!");
00336       ASTDeserializationListener *DeserialListener =
00337           Consumer->GetASTDeserializationListener();
00338       bool DeleteDeserialListener = false;
00339       if (CI.getPreprocessorOpts().DumpDeserializedPCHDecls) {
00340         DeserialListener = new DeserializedDeclsDumper(DeserialListener,
00341                                                        DeleteDeserialListener);
00342         DeleteDeserialListener = true;
00343       }
00344       if (!CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn.empty()) {
00345         DeserialListener = new DeserializedDeclsChecker(
00346             CI.getASTContext(),
00347             CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn,
00348             DeserialListener, DeleteDeserialListener);
00349         DeleteDeserialListener = true;
00350       }
00351       CI.createPCHExternalASTSource(
00352           CI.getPreprocessorOpts().ImplicitPCHInclude,
00353           CI.getPreprocessorOpts().DisablePCHValidation,
00354           CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, DeserialListener,
00355           DeleteDeserialListener);
00356       if (!CI.getASTContext().getExternalSource())
00357         goto failure;
00358     }
00359 
00360     CI.setASTConsumer(std::move(Consumer));
00361     if (!CI.hasASTConsumer())
00362       goto failure;
00363   }
00364 
00365   // Initialize built-in info as long as we aren't using an external AST
00366   // source.
00367   if (!CI.hasASTContext() || !CI.getASTContext().getExternalSource()) {
00368     Preprocessor &PP = CI.getPreprocessor();
00369 
00370     // If modules are enabled, create the module manager before creating
00371     // any builtins, so that all declarations know that they might be
00372     // extended by an external source.
00373     if (CI.getLangOpts().Modules)
00374       CI.createModuleManager();
00375 
00376     PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(),
00377                                            PP.getLangOpts());
00378   } else {
00379     // FIXME: If this is a problem, recover from it by creating a multiplex
00380     // source.
00381     assert((!CI.getLangOpts().Modules || CI.getModuleManager()) &&
00382            "modules enabled but created an external source that "
00383            "doesn't support modules");
00384   }
00385 
00386   // If we were asked to load any module files, do so now.
00387   for (const auto &ModuleFile : CI.getFrontendOpts().ModuleFiles)
00388     if (!CI.loadModuleFile(ModuleFile))
00389       goto failure;
00390 
00391   // If there is a layout overrides file, attach an external AST source that
00392   // provides the layouts from that file.
00393   if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() && 
00394       CI.hasASTContext() && !CI.getASTContext().getExternalSource()) {
00395     IntrusiveRefCntPtr<ExternalASTSource> 
00396       Override(new LayoutOverrideSource(
00397                      CI.getFrontendOpts().OverrideRecordLayoutsFile));
00398     CI.getASTContext().setExternalSource(Override);
00399   }
00400 
00401   return true;
00402 
00403   // If we failed, reset state since the client will not end up calling the
00404   // matching EndSourceFile().
00405   failure:
00406   if (isCurrentFileAST()) {
00407     CI.setASTContext(nullptr);
00408     CI.setPreprocessor(nullptr);
00409     CI.setSourceManager(nullptr);
00410     CI.setFileManager(nullptr);
00411   }
00412 
00413   if (HasBegunSourceFile)
00414     CI.getDiagnosticClient().EndSourceFile();
00415   CI.clearOutputFiles(/*EraseFiles=*/true);
00416   setCurrentInput(FrontendInputFile());
00417   setCompilerInstance(nullptr);
00418   return false;
00419 }
00420 
00421 bool FrontendAction::Execute() {
00422   CompilerInstance &CI = getCompilerInstance();
00423 
00424   if (CI.hasFrontendTimer()) {
00425     llvm::TimeRegion Timer(CI.getFrontendTimer());
00426     ExecuteAction();
00427   }
00428   else ExecuteAction();
00429 
00430   // If we are supposed to rebuild the global module index, do so now unless
00431   // there were any module-build failures.
00432   if (CI.shouldBuildGlobalModuleIndex() && CI.hasFileManager() &&
00433       CI.hasPreprocessor()) {
00434     GlobalModuleIndex::writeIndex(
00435       CI.getFileManager(),
00436       CI.getPreprocessor().getHeaderSearchInfo().getModuleCachePath());
00437   }
00438 
00439   return true;
00440 }
00441 
00442 void FrontendAction::EndSourceFile() {
00443   CompilerInstance &CI = getCompilerInstance();
00444 
00445   // Inform the diagnostic client we are done with this source file.
00446   CI.getDiagnosticClient().EndSourceFile();
00447 
00448   // Inform the preprocessor we are done.
00449   if (CI.hasPreprocessor())
00450     CI.getPreprocessor().EndSourceFile();
00451 
00452   // Finalize the action.
00453   EndSourceFileAction();
00454 
00455   // Sema references the ast consumer, so reset sema first.
00456   //
00457   // FIXME: There is more per-file stuff we could just drop here?
00458   bool DisableFree = CI.getFrontendOpts().DisableFree;
00459   if (DisableFree) {
00460     if (!isCurrentFileAST()) {
00461       CI.resetAndLeakSema();
00462       CI.resetAndLeakASTContext();
00463     }
00464     BuryPointer(CI.takeASTConsumer().get());
00465   } else {
00466     if (!isCurrentFileAST()) {
00467       CI.setSema(nullptr);
00468       CI.setASTContext(nullptr);
00469     }
00470     CI.setASTConsumer(nullptr);
00471   }
00472 
00473   if (CI.getFrontendOpts().ShowStats) {
00474     llvm::errs() << "\nSTATISTICS FOR '" << getCurrentFile() << "':\n";
00475     CI.getPreprocessor().PrintStats();
00476     CI.getPreprocessor().getIdentifierTable().PrintStats();
00477     CI.getPreprocessor().getHeaderSearchInfo().PrintStats();
00478     CI.getSourceManager().PrintStats();
00479     llvm::errs() << "\n";
00480   }
00481 
00482   // Cleanup the output streams, and erase the output files if instructed by the
00483   // FrontendAction.
00484   CI.clearOutputFiles(/*EraseFiles=*/shouldEraseOutputFiles());
00485 
00486   // FIXME: Only do this if DisableFree is set.
00487   if (isCurrentFileAST()) {
00488     CI.resetAndLeakSema();
00489     CI.resetAndLeakASTContext();
00490     CI.resetAndLeakPreprocessor();
00491     CI.resetAndLeakSourceManager();
00492     CI.resetAndLeakFileManager();
00493   }
00494 
00495   setCompilerInstance(nullptr);
00496   setCurrentInput(FrontendInputFile());
00497 }
00498 
00499 bool FrontendAction::shouldEraseOutputFiles() {
00500   return getCompilerInstance().getDiagnostics().hasErrorOccurred();
00501 }
00502 
00503 //===----------------------------------------------------------------------===//
00504 // Utility Actions
00505 //===----------------------------------------------------------------------===//
00506 
00507 void ASTFrontendAction::ExecuteAction() {
00508   CompilerInstance &CI = getCompilerInstance();
00509   if (!CI.hasPreprocessor())
00510     return;
00511 
00512   // FIXME: Move the truncation aspect of this into Sema, we delayed this till
00513   // here so the source manager would be initialized.
00514   if (hasCodeCompletionSupport() &&
00515       !CI.getFrontendOpts().CodeCompletionAt.FileName.empty())
00516     CI.createCodeCompletionConsumer();
00517 
00518   // Use a code completion consumer?
00519   CodeCompleteConsumer *CompletionConsumer = nullptr;
00520   if (CI.hasCodeCompletionConsumer())
00521     CompletionConsumer = &CI.getCodeCompletionConsumer();
00522 
00523   if (!CI.hasSema())
00524     CI.createSema(getTranslationUnitKind(), CompletionConsumer);
00525 
00526   ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,
00527            CI.getFrontendOpts().SkipFunctionBodies);
00528 }
00529 
00530 void PluginASTAction::anchor() { }
00531 
00532 std::unique_ptr<ASTConsumer>
00533 PreprocessorFrontendAction::CreateASTConsumer(CompilerInstance &CI,
00534                                               StringRef InFile) {
00535   llvm_unreachable("Invalid CreateASTConsumer on preprocessor action!");
00536 }
00537 
00538 std::unique_ptr<ASTConsumer>
00539 WrapperFrontendAction::CreateASTConsumer(CompilerInstance &CI,
00540                                          StringRef InFile) {
00541   return WrappedAction->CreateASTConsumer(CI, InFile);
00542 }
00543 bool WrapperFrontendAction::BeginInvocation(CompilerInstance &CI) {
00544   return WrappedAction->BeginInvocation(CI);
00545 }
00546 bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI,
00547                                                   StringRef Filename) {
00548   WrappedAction->setCurrentInput(getCurrentInput());
00549   WrappedAction->setCompilerInstance(&CI);
00550   return WrappedAction->BeginSourceFileAction(CI, Filename);
00551 }
00552 void WrapperFrontendAction::ExecuteAction() {
00553   WrappedAction->ExecuteAction();
00554 }
00555 void WrapperFrontendAction::EndSourceFileAction() {
00556   WrappedAction->EndSourceFileAction();
00557 }
00558 
00559 bool WrapperFrontendAction::usesPreprocessorOnly() const {
00560   return WrappedAction->usesPreprocessorOnly();
00561 }
00562 TranslationUnitKind WrapperFrontendAction::getTranslationUnitKind() {
00563   return WrappedAction->getTranslationUnitKind();
00564 }
00565 bool WrapperFrontendAction::hasPCHSupport() const {
00566   return WrappedAction->hasPCHSupport();
00567 }
00568 bool WrapperFrontendAction::hasASTFileSupport() const {
00569   return WrappedAction->hasASTFileSupport();
00570 }
00571 bool WrapperFrontendAction::hasIRSupport() const {
00572   return WrappedAction->hasIRSupport();
00573 }
00574 bool WrapperFrontendAction::hasCodeCompletionSupport() const {
00575   return WrappedAction->hasCodeCompletionSupport();
00576 }
00577 
00578 WrapperFrontendAction::WrapperFrontendAction(FrontendAction *WrappedAction)
00579   : WrappedAction(WrappedAction) {}
00580