FrontendAction.cpp revision 243830
150397Sobrien//===--- FrontendAction.cpp -----------------------------------------------===// 2169689Skan// 3132718Skan// The LLVM Compiler Infrastructure 450397Sobrien// 5132718Skan// This file is distributed under the University of Illinois Open Source 650397Sobrien// License. See LICENSE.TXT for details. 7132718Skan// 850397Sobrien//===----------------------------------------------------------------------===// 950397Sobrien 1050397Sobrien#include "clang/Frontend/FrontendAction.h" 1150397Sobrien#include "clang/AST/ASTConsumer.h" 12132718Skan#include "clang/AST/ASTContext.h" 1350397Sobrien#include "clang/AST/DeclGroup.h" 1450397Sobrien#include "clang/Lex/HeaderSearch.h" 1550397Sobrien#include "clang/Lex/Preprocessor.h" 1650397Sobrien#include "clang/Frontend/ASTUnit.h" 1750397Sobrien#include "clang/Frontend/ChainedIncludesSource.h" 18132718Skan#include "clang/Frontend/CompilerInstance.h" 19169689Skan#include "clang/Frontend/FrontendDiagnostic.h" 20169689Skan#include "clang/Frontend/FrontendPluginRegistry.h" 2150397Sobrien#include "clang/Frontend/LayoutOverrideSource.h" 2250397Sobrien#include "clang/Frontend/MultiplexConsumer.h" 2350397Sobrien#include "clang/Parse/ParseAST.h" 24132718Skan#include "clang/Serialization/ASTDeserializationListener.h" 25132718Skan#include "clang/Serialization/ASTReader.h" 2690075Sobrien#include "llvm/Support/ErrorHandling.h" 2750397Sobrien#include "llvm/Support/FileSystem.h" 2850397Sobrien#include "llvm/Support/MemoryBuffer.h" 2950397Sobrien#include "llvm/Support/raw_ostream.h" 3050397Sobrien#include "llvm/Support/system_error.h" 3150397Sobrien#include "llvm/Support/Timer.h" 3250397Sobrienusing namespace clang; 3350397Sobrien 3450397Sobriennamespace { 3550397Sobrien 3650397Sobrienclass DelegatingDeserializationListener : public ASTDeserializationListener { 3750397Sobrien ASTDeserializationListener *Previous; 3896263Sobrien 39169689Skanpublic: 4096263Sobrien explicit DelegatingDeserializationListener( 4152284Sobrien ASTDeserializationListener *Previous) 4252284Sobrien : Previous(Previous) { } 4352284Sobrien 4450397Sobrien virtual void ReaderInitialized(ASTReader *Reader) { 4596263Sobrien if (Previous) 46169689Skan Previous->ReaderInitialized(Reader); 4796263Sobrien } 4850397Sobrien virtual void IdentifierRead(serialization::IdentID ID, 4950397Sobrien IdentifierInfo *II) { 50132718Skan if (Previous) 51132718Skan Previous->IdentifierRead(ID, II); 5250397Sobrien } 5350397Sobrien virtual void TypeRead(serialization::TypeIdx Idx, QualType T) { 5450397Sobrien if (Previous) 55117395Skan Previous->TypeRead(Idx, T); 5696263Sobrien } 5796263Sobrien virtual void DeclRead(serialization::DeclID ID, const Decl *D) { 58117395Skan if (Previous) 5950397Sobrien Previous->DeclRead(ID, D); 6050397Sobrien } 61132718Skan virtual void SelectorRead(serialization::SelectorID ID, Selector Sel) { 62132718Skan if (Previous) 63132718Skan Previous->SelectorRead(ID, Sel); 64132718Skan } 65132718Skan virtual void MacroDefinitionRead(serialization::PreprocessedEntityID PPID, 6650397Sobrien MacroDefinition *MD) { 6750397Sobrien if (Previous) 6850397Sobrien Previous->MacroDefinitionRead(PPID, MD); 6950397Sobrien } 70132718Skan}; 7150397Sobrien 7250397Sobrien/// \brief Dumps deserialized declarations. 7350397Sobrienclass DeserializedDeclsDumper : public DelegatingDeserializationListener { 7490075Sobrienpublic: 7550397Sobrien explicit DeserializedDeclsDumper(ASTDeserializationListener *Previous) 7650397Sobrien : DelegatingDeserializationListener(Previous) { } 7790075Sobrien 7850397Sobrien virtual void DeclRead(serialization::DeclID ID, const Decl *D) { 79117395Skan llvm::outs() << "PCH DECL: " << D->getDeclKindName(); 8050397Sobrien if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) 8150397Sobrien llvm::outs() << " - " << *ND; 8250397Sobrien llvm::outs() << "\n"; 8350397Sobrien 8450397Sobrien DelegatingDeserializationListener::DeclRead(ID, D); 8590075Sobrien } 8650397Sobrien}; 8750397Sobrien 8890075Sobrien/// \brief Checks deserialized declarations and emits error if a name 8950397Sobrien/// matches one given in command-line using -error-on-deserialized-decl. 9050397Sobrienclass DeserializedDeclsChecker : public DelegatingDeserializationListener { 9150397Sobrien ASTContext &Ctx; 9250397Sobrien std::set<std::string> NamesToCheck; 9350397Sobrien 9452284Sobrienpublic: 9552284Sobrien DeserializedDeclsChecker(ASTContext &Ctx, 9650397Sobrien const std::set<std::string> &NamesToCheck, 9790075Sobrien ASTDeserializationListener *Previous) 9890075Sobrien : DelegatingDeserializationListener(Previous), 9990075Sobrien Ctx(Ctx), NamesToCheck(NamesToCheck) { } 10050397Sobrien 10150397Sobrien virtual void DeclRead(serialization::DeclID ID, const Decl *D) { 10250397Sobrien if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) 10350397Sobrien if (NamesToCheck.find(ND->getNameAsString()) != NamesToCheck.end()) { 10490075Sobrien unsigned DiagID 10550397Sobrien = Ctx.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, 10650397Sobrien "%0 was deserialized"); 10750397Sobrien Ctx.getDiagnostics().Report(Ctx.getFullLoc(D->getLocation()), DiagID) 10850397Sobrien << ND->getNameAsString(); 10950397Sobrien } 11050397Sobrien 11150397Sobrien DelegatingDeserializationListener::DeclRead(ID, D); 11250397Sobrien } 11350397Sobrien}; 11450397Sobrien 11550397Sobrien} // end anonymous namespace 116169689Skan 11750397SobrienFrontendAction::FrontendAction() : Instance(0) {} 11850397Sobrien 11950397SobrienFrontendAction::~FrontendAction() {} 12050397Sobrien 12150397Sobrienvoid FrontendAction::setCurrentInput(const FrontendInputFile &CurrentInput, 12250397Sobrien ASTUnit *AST) { 12350397Sobrien this->CurrentInput = CurrentInput; 12450397Sobrien CurrentASTUnit.reset(AST); 12550397Sobrien} 12650397Sobrien 12750397SobrienASTConsumer* FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI, 12850397Sobrien StringRef InFile) { 12950397Sobrien ASTConsumer* Consumer = CreateASTConsumer(CI, InFile); 13050397Sobrien if (!Consumer) 13150397Sobrien return 0; 13250397Sobrien 13350397Sobrien if (CI.getFrontendOpts().AddPluginActions.size() == 0) 134132718Skan return Consumer; 135132718Skan 13650397Sobrien // Make sure the non-plugin consumer is first, so that plugins can't 137132718Skan // modifiy the AST. 13850397Sobrien std::vector<ASTConsumer*> Consumers(1, Consumer); 139169689Skan 14050397Sobrien for (size_t i = 0, e = CI.getFrontendOpts().AddPluginActions.size(); 14150397Sobrien i != e; ++i) { 14250397Sobrien // This is O(|plugins| * |add_plugins|), but since both numbers are 14350397Sobrien // way below 50 in practice, that's ok. 14450397Sobrien for (FrontendPluginRegistry::iterator 14550397Sobrien it = FrontendPluginRegistry::begin(), 14696263Sobrien ie = FrontendPluginRegistry::end(); 14796263Sobrien it != ie; ++it) { 14850397Sobrien if (it->getName() == CI.getFrontendOpts().AddPluginActions[i]) { 149132718Skan OwningPtr<PluginASTAction> P(it->instantiate()); 150132718Skan FrontendAction* c = P.get(); 15150397Sobrien if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i])) 152169689Skan Consumers.push_back(c->CreateASTConsumer(CI, InFile)); 153169689Skan } 154169689Skan } 155169689Skan } 156169689Skan 157169689Skan return new MultiplexConsumer(Consumers); 158169689Skan} 159169689Skan 160169689Skan 161169689Skanbool FrontendAction::BeginSourceFile(CompilerInstance &CI, 162169689Skan const FrontendInputFile &Input) { 163169689Skan assert(!Instance && "Already processing a source file!"); 164169689Skan assert(!Input.isEmpty() && "Unexpected empty filename!"); 165132718Skan setCurrentInput(Input); 16650397Sobrien setCompilerInstance(&CI); 167169689Skan 168169689Skan StringRef InputFile = Input.getFile(); 169169689Skan bool HasBegunSourceFile = false; 170169689Skan if (!BeginInvocation(CI)) 171169689Skan goto failure; 172169689Skan 173169689Skan // AST files follow a very different path, since they share objects via the 174169689Skan // AST unit. 175169689Skan if (Input.getKind() == IK_AST) { 176169689Skan assert(!usesPreprocessorOnly() && 177146895Skan "Attempt to pass AST file to preprocessor only action!"); 178146895Skan assert(hasASTFileSupport() && 179146895Skan "This action does not have AST file support!"); 18050397Sobrien 181132718Skan IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics()); 18250397Sobrien std::string Error; 18350397Sobrien ASTUnit *AST = ASTUnit::LoadFromASTFile(InputFile, Diags, 184132718Skan CI.getFileSystemOpts()); 185132718Skan if (!AST) 186132718Skan goto failure; 187132718Skan 18850397Sobrien setCurrentInput(Input, AST); 18950397Sobrien 19050397Sobrien // Set the shared objects, these are reset when we finish processing the 191132718Skan // file, otherwise the CompilerInstance will happily destroy them. 19250397Sobrien CI.setFileManager(&AST->getFileManager()); 193169689Skan CI.setSourceManager(&AST->getSourceManager()); 19490075Sobrien CI.setPreprocessor(&AST->getPreprocessor()); 19590075Sobrien CI.setASTContext(&AST->getASTContext()); 196117395Skan 197117395Skan // Initialize the action. 19850397Sobrien if (!BeginSourceFileAction(CI, InputFile)) 19950397Sobrien goto failure; 20050397Sobrien 20150397Sobrien /// Create the AST consumer. 20250397Sobrien CI.setASTConsumer(CreateWrappedASTConsumer(CI, InputFile)); 20350397Sobrien if (!CI.hasASTConsumer()) 204169689Skan goto failure; 20550397Sobrien 20650397Sobrien return true; 20750397Sobrien } 20850397Sobrien 20950397Sobrien // Set up the file and source managers, if needed. 21050397Sobrien if (!CI.hasFileManager()) 21150397Sobrien CI.createFileManager(); 212132718Skan if (!CI.hasSourceManager()) 21350397Sobrien CI.createSourceManager(CI.getFileManager()); 21450397Sobrien 21550397Sobrien // IR files bypass the rest of initialization. 216169689Skan if (Input.getKind() == IK_LLVM_IR) { 217132718Skan assert(hasIRSupport() && 218132718Skan "This action does not have IR file support!"); 21950397Sobrien 22050397Sobrien // Inform the diagnostic client we are processing a source file. 22150397Sobrien CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 0); 22250397Sobrien HasBegunSourceFile = true; 22350397Sobrien 224169689Skan // Initialize the action. 225132718Skan if (!BeginSourceFileAction(CI, InputFile)) 226132718Skan goto failure; 227132718Skan 228132718Skan return true; 229132718Skan } 230132718Skan 231132718Skan // If the implicit PCH include is actually a directory, rather than 232132718Skan // a single file, search for a suitable PCH file in that directory. 233132718Skan if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { 23450397Sobrien FileManager &FileMgr = CI.getFileManager(); 23550397Sobrien PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); 23650397Sobrien StringRef PCHInclude = PPOpts.ImplicitPCHInclude; 23750397Sobrien if (const DirectoryEntry *PCHDir = FileMgr.getDirectory(PCHInclude)) { 238169689Skan llvm::error_code EC; 23950397Sobrien SmallString<128> DirNative; 24050397Sobrien llvm::sys::path::native(PCHDir->getName(), DirNative); 241132718Skan bool Found = false; 24250397Sobrien for (llvm::sys::fs::directory_iterator Dir(DirNative.str(), EC), DirEnd; 24350397Sobrien Dir != DirEnd && !EC; Dir.increment(EC)) { 24450397Sobrien // Check whether this is an acceptable AST file. 24550397Sobrien if (ASTReader::isAcceptableASTFile(Dir->path(), FileMgr, 24650397Sobrien CI.getLangOpts(), 24790075Sobrien CI.getTargetOpts(), 24890075Sobrien CI.getPreprocessorOpts())) { 24990075Sobrien for (unsigned I = 0, N = PPOpts.Includes.size(); I != N; ++I) { 25090075Sobrien if (PPOpts.Includes[I] == PPOpts.ImplicitPCHInclude) { 25190075Sobrien PPOpts.Includes[I] = Dir->path(); 25250397Sobrien PPOpts.ImplicitPCHInclude = Dir->path(); 25390075Sobrien Found = true; 254132718Skan break; 255169689Skan } 25690075Sobrien } 25790075Sobrien 25890075Sobrien assert(Found && "Implicit PCH include not in includes list?"); 259169689Skan break; 26090075Sobrien } 26190075Sobrien } 26290075Sobrien 26350397Sobrien if (!Found) { 26490075Sobrien CI.getDiagnostics().Report(diag::err_fe_no_pch_in_dir) << PCHInclude; 26550397Sobrien return true; 26650397Sobrien } 26750397Sobrien } 26850397Sobrien } 26950397Sobrien 270132718Skan // Set up the preprocessor. 27150397Sobrien CI.createPreprocessor(); 27250397Sobrien 27350397Sobrien // Inform the diagnostic client we are processing a source file. 27450397Sobrien CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 27550397Sobrien &CI.getPreprocessor()); 276132718Skan HasBegunSourceFile = true; 27750397Sobrien 27850397Sobrien // Initialize the action. 27950397Sobrien if (!BeginSourceFileAction(CI, InputFile)) 28050397Sobrien goto failure; 28150397Sobrien 282132718Skan /// Create the AST context and consumer unless this is a preprocessor only 28350397Sobrien /// action. 28450397Sobrien if (!usesPreprocessorOnly()) { 28550397Sobrien CI.createASTContext(); 28650397Sobrien 287132718Skan OwningPtr<ASTConsumer> Consumer( 288132718Skan CreateWrappedASTConsumer(CI, InputFile)); 289132718Skan if (!Consumer) 290132718Skan goto failure; 291132718Skan 292132718Skan CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener()); 293132718Skan CI.getPreprocessor().setPPMutationListener( 294132718Skan Consumer->GetPPMutationListener()); 295132718Skan 296132718Skan if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) { 297132718Skan // Convert headers to PCH and chain them. 298132718Skan OwningPtr<ExternalASTSource> source; 299169689Skan source.reset(ChainedIncludesSource::create(CI)); 300132718Skan if (!source) 30150397Sobrien goto failure; 30250397Sobrien CI.getASTContext().setExternalSource(source); 30350397Sobrien 30450397Sobrien } else if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { 30590075Sobrien // Use PCH. 30690075Sobrien assert(hasPCHSupport() && "This action does not have PCH support!"); 30790075Sobrien ASTDeserializationListener *DeserialListener = 30890075Sobrien Consumer->GetASTDeserializationListener(); 30950397Sobrien if (CI.getPreprocessorOpts().DumpDeserializedPCHDecls) 310132718Skan DeserialListener = new DeserializedDeclsDumper(DeserialListener); 31150397Sobrien if (!CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn.empty()) 312169689Skan DeserialListener = new DeserializedDeclsChecker(CI.getASTContext(), 313169689Skan CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn, 314169689Skan DeserialListener); 315169689Skan CI.createPCHExternalASTSource( 31650397Sobrien CI.getPreprocessorOpts().ImplicitPCHInclude, 31750397Sobrien CI.getPreprocessorOpts().DisablePCHValidation, 31850397Sobrien CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, 319132718Skan DeserialListener); 32050397Sobrien if (!CI.getASTContext().getExternalSource()) 321169689Skan goto failure; 322169689Skan } 323169689Skan 324169689Skan CI.setASTConsumer(Consumer.take()); 32550397Sobrien if (!CI.hasASTConsumer()) 32650397Sobrien goto failure; 32750397Sobrien } 32890075Sobrien 32990075Sobrien // Initialize built-in info as long as we aren't using an external AST 33050397Sobrien // source. 33150397Sobrien if (!CI.hasASTContext() || !CI.getASTContext().getExternalSource()) { 33250397Sobrien Preprocessor &PP = CI.getPreprocessor(); 33350397Sobrien PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(), 33450397Sobrien PP.getLangOpts()); 33550397Sobrien } 33650397Sobrien 33750397Sobrien // If there is a layout overrides file, attach an external AST source that 338117395Skan // provides the layouts from that file. 339132718Skan if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() && 34050397Sobrien CI.hasASTContext() && !CI.getASTContext().getExternalSource()) { 34150397Sobrien OwningPtr<ExternalASTSource> 34250397Sobrien Override(new LayoutOverrideSource( 34350397Sobrien CI.getFrontendOpts().OverrideRecordLayoutsFile)); 344117395Skan CI.getASTContext().setExternalSource(Override); 345117395Skan } 346 347 return true; 348 349 // If we failed, reset state since the client will not end up calling the 350 // matching EndSourceFile(). 351 failure: 352 if (isCurrentFileAST()) { 353 CI.setASTContext(0); 354 CI.setPreprocessor(0); 355 CI.setSourceManager(0); 356 CI.setFileManager(0); 357 } 358 359 if (HasBegunSourceFile) 360 CI.getDiagnosticClient().EndSourceFile(); 361 CI.clearOutputFiles(/*EraseFiles=*/true); 362 setCurrentInput(FrontendInputFile()); 363 setCompilerInstance(0); 364 return false; 365} 366 367bool FrontendAction::Execute() { 368 CompilerInstance &CI = getCompilerInstance(); 369 370 // Initialize the main file entry. This needs to be delayed until after PCH 371 // has loaded. 372 if (!isCurrentFileAST()) { 373 if (!CI.InitializeSourceManager(getCurrentInput())) 374 return false; 375 } 376 377 if (CI.hasFrontendTimer()) { 378 llvm::TimeRegion Timer(CI.getFrontendTimer()); 379 ExecuteAction(); 380 } 381 else ExecuteAction(); 382 383 return true; 384} 385 386void FrontendAction::EndSourceFile() { 387 CompilerInstance &CI = getCompilerInstance(); 388 389 // Inform the diagnostic client we are done with this source file. 390 CI.getDiagnosticClient().EndSourceFile(); 391 392 // Finalize the action. 393 EndSourceFileAction(); 394 395 // Release the consumer and the AST, in that order since the consumer may 396 // perform actions in its destructor which require the context. 397 // 398 // FIXME: There is more per-file stuff we could just drop here? 399 if (CI.getFrontendOpts().DisableFree) { 400 CI.takeASTConsumer(); 401 if (!isCurrentFileAST()) { 402 CI.takeSema(); 403 CI.resetAndLeakASTContext(); 404 } 405 } else { 406 if (!isCurrentFileAST()) { 407 CI.setSema(0); 408 CI.setASTContext(0); 409 } 410 CI.setASTConsumer(0); 411 } 412 413 // Inform the preprocessor we are done. 414 if (CI.hasPreprocessor()) 415 CI.getPreprocessor().EndSourceFile(); 416 417 if (CI.getFrontendOpts().ShowStats) { 418 llvm::errs() << "\nSTATISTICS FOR '" << getCurrentFile() << "':\n"; 419 CI.getPreprocessor().PrintStats(); 420 CI.getPreprocessor().getIdentifierTable().PrintStats(); 421 CI.getPreprocessor().getHeaderSearchInfo().PrintStats(); 422 CI.getSourceManager().PrintStats(); 423 llvm::errs() << "\n"; 424 } 425 426 // Cleanup the output streams, and erase the output files if we encountered 427 // an error. 428 CI.clearOutputFiles(/*EraseFiles=*/CI.getDiagnostics().hasErrorOccurred()); 429 430 if (isCurrentFileAST()) { 431 CI.takeSema(); 432 CI.resetAndLeakASTContext(); 433 CI.resetAndLeakPreprocessor(); 434 CI.resetAndLeakSourceManager(); 435 CI.resetAndLeakFileManager(); 436 } 437 438 setCompilerInstance(0); 439 setCurrentInput(FrontendInputFile()); 440} 441 442//===----------------------------------------------------------------------===// 443// Utility Actions 444//===----------------------------------------------------------------------===// 445 446void ASTFrontendAction::ExecuteAction() { 447 CompilerInstance &CI = getCompilerInstance(); 448 449 // FIXME: Move the truncation aspect of this into Sema, we delayed this till 450 // here so the source manager would be initialized. 451 if (hasCodeCompletionSupport() && 452 !CI.getFrontendOpts().CodeCompletionAt.FileName.empty()) 453 CI.createCodeCompletionConsumer(); 454 455 // Use a code completion consumer? 456 CodeCompleteConsumer *CompletionConsumer = 0; 457 if (CI.hasCodeCompletionConsumer()) 458 CompletionConsumer = &CI.getCodeCompletionConsumer(); 459 460 if (!CI.hasSema()) 461 CI.createSema(getTranslationUnitKind(), CompletionConsumer); 462 463 ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats, 464 CI.getFrontendOpts().SkipFunctionBodies); 465} 466 467void PluginASTAction::anchor() { } 468 469ASTConsumer * 470PreprocessorFrontendAction::CreateASTConsumer(CompilerInstance &CI, 471 StringRef InFile) { 472 llvm_unreachable("Invalid CreateASTConsumer on preprocessor action!"); 473} 474 475ASTConsumer *WrapperFrontendAction::CreateASTConsumer(CompilerInstance &CI, 476 StringRef InFile) { 477 return WrappedAction->CreateASTConsumer(CI, InFile); 478} 479bool WrapperFrontendAction::BeginInvocation(CompilerInstance &CI) { 480 return WrappedAction->BeginInvocation(CI); 481} 482bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI, 483 StringRef Filename) { 484 WrappedAction->setCurrentInput(getCurrentInput()); 485 WrappedAction->setCompilerInstance(&CI); 486 return WrappedAction->BeginSourceFileAction(CI, Filename); 487} 488void WrapperFrontendAction::ExecuteAction() { 489 WrappedAction->ExecuteAction(); 490} 491void WrapperFrontendAction::EndSourceFileAction() { 492 WrappedAction->EndSourceFileAction(); 493} 494 495bool WrapperFrontendAction::usesPreprocessorOnly() const { 496 return WrappedAction->usesPreprocessorOnly(); 497} 498TranslationUnitKind WrapperFrontendAction::getTranslationUnitKind() { 499 return WrappedAction->getTranslationUnitKind(); 500} 501bool WrapperFrontendAction::hasPCHSupport() const { 502 return WrappedAction->hasPCHSupport(); 503} 504bool WrapperFrontendAction::hasASTFileSupport() const { 505 return WrappedAction->hasASTFileSupport(); 506} 507bool WrapperFrontendAction::hasIRSupport() const { 508 return WrappedAction->hasIRSupport(); 509} 510bool WrapperFrontendAction::hasCodeCompletionSupport() const { 511 return WrappedAction->hasCodeCompletionSupport(); 512} 513 514WrapperFrontendAction::WrapperFrontendAction(FrontendAction *WrappedAction) 515 : WrappedAction(WrappedAction) {} 516 517