Index: lib/Frontend/ASTUnit.cpp =================================================================== --- lib/Frontend/ASTUnit.cpp (revision 305060) +++ lib/Frontend/ASTUnit.cpp (revision 305061) @@ -1,2966 +1,2971 @@ //===--- ASTUnit.cpp - ASTUnit utility --------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // ASTUnit Implementation. // //===----------------------------------------------------------------------===// #include "clang/Frontend/ASTUnit.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeOrdering.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/MemoryBufferCache.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" #include "clang/Basic/VirtualFileSystem.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Frontend/MultiplexConsumer.h" #include "clang/Frontend/Utils.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/Sema.h" #include "clang/Serialization/ASTReader.h" #include "clang/Serialization/ASTWriter.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Mutex.h" #include "llvm/Support/MutexGuard.h" #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace clang; using llvm::TimeRecord; namespace { class SimpleTimer { bool WantTiming; TimeRecord Start; std::string Output; public: explicit SimpleTimer(bool WantTiming) : WantTiming(WantTiming) { if (WantTiming) Start = TimeRecord::getCurrentTime(); } void setOutput(const Twine &Output) { if (WantTiming) this->Output = Output.str(); } ~SimpleTimer() { if (WantTiming) { TimeRecord Elapsed = TimeRecord::getCurrentTime(); Elapsed -= Start; llvm::errs() << Output << ':'; Elapsed.print(Elapsed, llvm::errs()); llvm::errs() << '\n'; } } }; struct OnDiskData { /// \brief The file in which the precompiled preamble is stored. std::string PreambleFile; /// \brief Erase the preamble file. void CleanPreambleFile(); /// \brief Erase temporary files and the preamble file. void Cleanup(); }; template std::unique_ptr valueOrNull(llvm::ErrorOr> Val) { if (!Val) return nullptr; return std::move(*Val); } template bool moveOnNoError(llvm::ErrorOr Val, T &Output) { if (!Val) return false; Output = std::move(*Val); return true; } } static llvm::sys::SmartMutex &getOnDiskMutex() { static llvm::sys::SmartMutex M(/* recursive = */ true); return M; } static void cleanupOnDiskMapAtExit(); typedef llvm::DenseMap> OnDiskDataMap; static OnDiskDataMap &getOnDiskDataMap() { static OnDiskDataMap M; static bool hasRegisteredAtExit = false; if (!hasRegisteredAtExit) { hasRegisteredAtExit = true; atexit(cleanupOnDiskMapAtExit); } return M; } static void cleanupOnDiskMapAtExit() { // Use the mutex because there can be an alive thread destroying an ASTUnit. llvm::MutexGuard Guard(getOnDiskMutex()); for (const auto &I : getOnDiskDataMap()) { // We don't worry about freeing the memory associated with OnDiskDataMap. // All we care about is erasing stale files. I.second->Cleanup(); } } static OnDiskData &getOnDiskData(const ASTUnit *AU) { // We require the mutex since we are modifying the structure of the // DenseMap. llvm::MutexGuard Guard(getOnDiskMutex()); OnDiskDataMap &M = getOnDiskDataMap(); auto &D = M[AU]; if (!D) D = llvm::make_unique(); return *D; } static void erasePreambleFile(const ASTUnit *AU) { getOnDiskData(AU).CleanPreambleFile(); } static void removeOnDiskEntry(const ASTUnit *AU) { // We require the mutex since we are modifying the structure of the // DenseMap. llvm::MutexGuard Guard(getOnDiskMutex()); OnDiskDataMap &M = getOnDiskDataMap(); OnDiskDataMap::iterator I = M.find(AU); if (I != M.end()) { I->second->Cleanup(); M.erase(I); } } static void setPreambleFile(const ASTUnit *AU, StringRef preambleFile) { getOnDiskData(AU).PreambleFile = preambleFile; } static const std::string &getPreambleFile(const ASTUnit *AU) { return getOnDiskData(AU).PreambleFile; } void OnDiskData::CleanPreambleFile() { if (!PreambleFile.empty()) { llvm::sys::fs::remove(PreambleFile); PreambleFile.clear(); } } void OnDiskData::Cleanup() { CleanPreambleFile(); } struct ASTUnit::ASTWriterData { SmallString<128> Buffer; llvm::BitstreamWriter Stream; ASTWriter Writer; ASTWriterData(MemoryBufferCache &PCMCache) : Stream(Buffer), Writer(Stream, Buffer, PCMCache, {}) {} }; void ASTUnit::clearFileLevelDecls() { llvm::DeleteContainerSeconds(FileDecls); } /// \brief After failing to build a precompiled preamble (due to /// errors in the source that occurs in the preamble), the number of /// reparses during which we'll skip even trying to precompile the /// preamble. const unsigned DefaultPreambleRebuildInterval = 5; /// \brief Tracks the number of ASTUnit objects that are currently active. /// /// Used for debugging purposes only. static std::atomic ActiveASTUnitObjects; ASTUnit::ASTUnit(bool _MainFileIsAST) : Reader(nullptr), HadModuleLoaderFatalFailure(false), OnlyLocalDecls(false), CaptureDiagnostics(false), MainFileIsAST(_MainFileIsAST), TUKind(TU_Complete), WantTiming(getenv("LIBCLANG_TIMING")), OwnsRemappedFileBuffers(true), NumStoredDiagnosticsFromDriver(0), PreambleRebuildCounter(0), NumWarningsInPreamble(0), ShouldCacheCodeCompletionResults(false), IncludeBriefCommentsInCodeCompletion(false), UserFilesAreVolatile(false), CompletionCacheTopLevelHashValue(0), PreambleTopLevelHashValue(0), CurrentTopLevelHashValue(0), UnsafeToFree(false) { if (getenv("LIBCLANG_OBJTRACKING")) fprintf(stderr, "+++ %u translation units\n", ++ActiveASTUnitObjects); } ASTUnit::~ASTUnit() { // If we loaded from an AST file, balance out the BeginSourceFile call. if (MainFileIsAST && getDiagnostics().getClient()) { getDiagnostics().getClient()->EndSourceFile(); } clearFileLevelDecls(); // Clean up the temporary files and the preamble file. removeOnDiskEntry(this); // Free the buffers associated with remapped files. We are required to // perform this operation here because we explicitly request that the // compiler instance *not* free these buffers for each invocation of the // parser. if (Invocation && OwnsRemappedFileBuffers) { PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); for (const auto &RB : PPOpts.RemappedFileBuffers) delete RB.second; } ClearCachedCompletionResults(); if (getenv("LIBCLANG_OBJTRACKING")) fprintf(stderr, "--- %u translation units\n", --ActiveASTUnitObjects); } void ASTUnit::setPreprocessor(std::shared_ptr PP) { this->PP = std::move(PP); } /// \brief Determine the set of code-completion contexts in which this /// declaration should be shown. static unsigned getDeclShowContexts(const NamedDecl *ND, const LangOptions &LangOpts, bool &IsNestedNameSpecifier) { IsNestedNameSpecifier = false; if (isa(ND)) ND = dyn_cast(ND->getUnderlyingDecl()); if (!ND) return 0; uint64_t Contexts = 0; if (isa(ND) || isa(ND) || isa(ND) || isa(ND)) { // Types can appear in these contexts. if (LangOpts.CPlusPlus || !isa(ND)) Contexts |= (1LL << CodeCompletionContext::CCC_TopLevel) | (1LL << CodeCompletionContext::CCC_ObjCIvarList) | (1LL << CodeCompletionContext::CCC_ClassStructUnion) | (1LL << CodeCompletionContext::CCC_Statement) | (1LL << CodeCompletionContext::CCC_Type) | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression); // In C++, types can appear in expressions contexts (for functional casts). if (LangOpts.CPlusPlus) Contexts |= (1LL << CodeCompletionContext::CCC_Expression); // In Objective-C, message sends can send interfaces. In Objective-C++, // all types are available due to functional casts. if (LangOpts.CPlusPlus || isa(ND)) Contexts |= (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver); // In Objective-C, you can only be a subclass of another Objective-C class if (isa(ND)) Contexts |= (1LL << CodeCompletionContext::CCC_ObjCInterfaceName); // Deal with tag names. if (isa(ND)) { Contexts |= (1LL << CodeCompletionContext::CCC_EnumTag); // Part of the nested-name-specifier in C++0x. if (LangOpts.CPlusPlus11) IsNestedNameSpecifier = true; } else if (const RecordDecl *Record = dyn_cast(ND)) { if (Record->isUnion()) Contexts |= (1LL << CodeCompletionContext::CCC_UnionTag); else Contexts |= (1LL << CodeCompletionContext::CCC_ClassOrStructTag); if (LangOpts.CPlusPlus) IsNestedNameSpecifier = true; } else if (isa(ND)) IsNestedNameSpecifier = true; } else if (isa(ND) || isa(ND)) { // Values can appear in these contexts. Contexts = (1LL << CodeCompletionContext::CCC_Statement) | (1LL << CodeCompletionContext::CCC_Expression) | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver); } else if (isa(ND)) { Contexts = (1LL << CodeCompletionContext::CCC_ObjCProtocolName); } else if (isa(ND)) { Contexts = (1LL << CodeCompletionContext::CCC_ObjCCategoryName); } else if (isa(ND) || isa(ND)) { Contexts = (1LL << CodeCompletionContext::CCC_Namespace); // Part of the nested-name-specifier. IsNestedNameSpecifier = true; } return Contexts; } void ASTUnit::CacheCodeCompletionResults() { if (!TheSema) return; SimpleTimer Timer(WantTiming); Timer.setOutput("Cache global code completions for " + getMainFileName()); // Clear out the previous results. ClearCachedCompletionResults(); // Gather the set of global code completions. typedef CodeCompletionResult Result; SmallVector Results; CachedCompletionAllocator = std::make_shared(); CodeCompletionTUInfo CCTUInfo(CachedCompletionAllocator); TheSema->GatherGlobalCodeCompletions(*CachedCompletionAllocator, CCTUInfo, Results); // Translate global code completions into cached completions. llvm::DenseMap CompletionTypes; CodeCompletionContext CCContext(CodeCompletionContext::CCC_TopLevel); for (Result &R : Results) { switch (R.Kind) { case Result::RK_Declaration: { bool IsNestedNameSpecifier = false; CachedCodeCompletionResult CachedResult; CachedResult.Completion = R.CreateCodeCompletionString( *TheSema, CCContext, *CachedCompletionAllocator, CCTUInfo, IncludeBriefCommentsInCodeCompletion); CachedResult.ShowInContexts = getDeclShowContexts( R.Declaration, Ctx->getLangOpts(), IsNestedNameSpecifier); CachedResult.Priority = R.Priority; CachedResult.Kind = R.CursorKind; CachedResult.Availability = R.Availability; // Keep track of the type of this completion in an ASTContext-agnostic // way. QualType UsageType = getDeclUsageType(*Ctx, R.Declaration); if (UsageType.isNull()) { CachedResult.TypeClass = STC_Void; CachedResult.Type = 0; } else { CanQualType CanUsageType = Ctx->getCanonicalType(UsageType.getUnqualifiedType()); CachedResult.TypeClass = getSimplifiedTypeClass(CanUsageType); // Determine whether we have already seen this type. If so, we save // ourselves the work of formatting the type string by using the // temporary, CanQualType-based hash table to find the associated value. unsigned &TypeValue = CompletionTypes[CanUsageType]; if (TypeValue == 0) { TypeValue = CompletionTypes.size(); CachedCompletionTypes[QualType(CanUsageType).getAsString()] = TypeValue; } CachedResult.Type = TypeValue; } CachedCompletionResults.push_back(CachedResult); /// Handle nested-name-specifiers in C++. if (TheSema->Context.getLangOpts().CPlusPlus && IsNestedNameSpecifier && !R.StartsNestedNameSpecifier) { // The contexts in which a nested-name-specifier can appear in C++. uint64_t NNSContexts = (1LL << CodeCompletionContext::CCC_TopLevel) | (1LL << CodeCompletionContext::CCC_ObjCIvarList) | (1LL << CodeCompletionContext::CCC_ClassStructUnion) | (1LL << CodeCompletionContext::CCC_Statement) | (1LL << CodeCompletionContext::CCC_Expression) | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) | (1LL << CodeCompletionContext::CCC_EnumTag) | (1LL << CodeCompletionContext::CCC_UnionTag) | (1LL << CodeCompletionContext::CCC_ClassOrStructTag) | (1LL << CodeCompletionContext::CCC_Type) | (1LL << CodeCompletionContext::CCC_PotentiallyQualifiedName) | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression); if (isa(R.Declaration) || isa(R.Declaration)) NNSContexts |= (1LL << CodeCompletionContext::CCC_Namespace); if (unsigned RemainingContexts = NNSContexts & ~CachedResult.ShowInContexts) { // If there any contexts where this completion can be a // nested-name-specifier but isn't already an option, create a // nested-name-specifier completion. R.StartsNestedNameSpecifier = true; CachedResult.Completion = R.CreateCodeCompletionString( *TheSema, CCContext, *CachedCompletionAllocator, CCTUInfo, IncludeBriefCommentsInCodeCompletion); CachedResult.ShowInContexts = RemainingContexts; CachedResult.Priority = CCP_NestedNameSpecifier; CachedResult.TypeClass = STC_Void; CachedResult.Type = 0; CachedCompletionResults.push_back(CachedResult); } } break; } case Result::RK_Keyword: case Result::RK_Pattern: // Ignore keywords and patterns; we don't care, since they are so // easily regenerated. break; case Result::RK_Macro: { CachedCodeCompletionResult CachedResult; CachedResult.Completion = R.CreateCodeCompletionString( *TheSema, CCContext, *CachedCompletionAllocator, CCTUInfo, IncludeBriefCommentsInCodeCompletion); CachedResult.ShowInContexts = (1LL << CodeCompletionContext::CCC_TopLevel) | (1LL << CodeCompletionContext::CCC_ObjCInterface) | (1LL << CodeCompletionContext::CCC_ObjCImplementation) | (1LL << CodeCompletionContext::CCC_ObjCIvarList) | (1LL << CodeCompletionContext::CCC_ClassStructUnion) | (1LL << CodeCompletionContext::CCC_Statement) | (1LL << CodeCompletionContext::CCC_Expression) | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) | (1LL << CodeCompletionContext::CCC_MacroNameUse) | (1LL << CodeCompletionContext::CCC_PreprocessorExpression) | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) | (1LL << CodeCompletionContext::CCC_OtherWithMacros); CachedResult.Priority = R.Priority; CachedResult.Kind = R.CursorKind; CachedResult.Availability = R.Availability; CachedResult.TypeClass = STC_Void; CachedResult.Type = 0; CachedCompletionResults.push_back(CachedResult); break; } } } // Save the current top-level hash value. CompletionCacheTopLevelHashValue = CurrentTopLevelHashValue; } void ASTUnit::ClearCachedCompletionResults() { CachedCompletionResults.clear(); CachedCompletionTypes.clear(); CachedCompletionAllocator = nullptr; } namespace { /// \brief Gathers information from ASTReader that will be used to initialize /// a Preprocessor. class ASTInfoCollector : public ASTReaderListener { Preprocessor &PP; ASTContext &Context; HeaderSearchOptions &HSOpts; PreprocessorOptions &PPOpts; LangOptions &LangOpt; std::shared_ptr &TargetOpts; IntrusiveRefCntPtr &Target; unsigned &Counter; bool InitializedLanguage; public: ASTInfoCollector(Preprocessor &PP, ASTContext &Context, HeaderSearchOptions &HSOpts, PreprocessorOptions &PPOpts, LangOptions &LangOpt, std::shared_ptr &TargetOpts, IntrusiveRefCntPtr &Target, unsigned &Counter) : PP(PP), Context(Context), HSOpts(HSOpts), PPOpts(PPOpts), LangOpt(LangOpt), TargetOpts(TargetOpts), Target(Target), Counter(Counter), InitializedLanguage(false) {} bool ReadLanguageOptions(const LangOptions &LangOpts, bool Complain, bool AllowCompatibleDifferences) override { if (InitializedLanguage) return false; LangOpt = LangOpts; InitializedLanguage = true; updated(); return false; } virtual bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts, StringRef SpecificModuleCachePath, bool Complain) override { this->HSOpts = HSOpts; return false; } virtual bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts, bool Complain, std::string &SuggestedPredefines) override { this->PPOpts = PPOpts; return false; } bool ReadTargetOptions(const TargetOptions &TargetOpts, bool Complain, bool AllowCompatibleDifferences) override { // If we've already initialized the target, don't do it again. if (Target) return false; this->TargetOpts = std::make_shared(TargetOpts); Target = TargetInfo::CreateTargetInfo(PP.getDiagnostics(), this->TargetOpts); updated(); return false; } void ReadCounter(const serialization::ModuleFile &M, unsigned Value) override { Counter = Value; } private: void updated() { if (!Target || !InitializedLanguage) return; // Inform the target of the language options. // // FIXME: We shouldn't need to do this, the target should be immutable once // created. This complexity should be lifted elsewhere. Target->adjust(LangOpt); // Initialize the preprocessor. PP.Initialize(*Target); // Initialize the ASTContext Context.InitBuiltinTypes(*Target); // We didn't have access to the comment options when the ASTContext was // constructed, so register them now. Context.getCommentCommandTraits().registerCommentOptions( LangOpt.CommentOpts); } }; /// \brief Diagnostic consumer that saves each diagnostic it is given. class StoredDiagnosticConsumer : public DiagnosticConsumer { SmallVectorImpl &StoredDiags; SourceManager *SourceMgr; public: explicit StoredDiagnosticConsumer( SmallVectorImpl &StoredDiags) : StoredDiags(StoredDiags), SourceMgr(nullptr) {} void BeginSourceFile(const LangOptions &LangOpts, const Preprocessor *PP = nullptr) override { if (PP) SourceMgr = &PP->getSourceManager(); } void HandleDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info) override; }; /// \brief RAII object that optionally captures diagnostics, if /// there is no diagnostic client to capture them already. class CaptureDroppedDiagnostics { DiagnosticsEngine &Diags; StoredDiagnosticConsumer Client; DiagnosticConsumer *PreviousClient; std::unique_ptr OwningPreviousClient; public: CaptureDroppedDiagnostics(bool RequestCapture, DiagnosticsEngine &Diags, SmallVectorImpl &StoredDiags) : Diags(Diags), Client(StoredDiags), PreviousClient(nullptr) { if (RequestCapture || Diags.getClient() == nullptr) { OwningPreviousClient = Diags.takeClient(); PreviousClient = Diags.getClient(); Diags.setClient(&Client, false); } } ~CaptureDroppedDiagnostics() { if (Diags.getClient() == &Client) Diags.setClient(PreviousClient, !!OwningPreviousClient.release()); } }; } // anonymous namespace void StoredDiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info) { // Default implementation (Warnings/errors count). DiagnosticConsumer::HandleDiagnostic(Level, Info); // Only record the diagnostic if it's part of the source manager we know // about. This effectively drops diagnostics from modules we're building. // FIXME: In the long run, ee don't want to drop source managers from modules. if (!Info.hasSourceManager() || &Info.getSourceManager() == SourceMgr) StoredDiags.emplace_back(Level, Info); } IntrusiveRefCntPtr ASTUnit::getASTReader() const { return Reader; } ASTMutationListener *ASTUnit::getASTMutationListener() { if (WriterData) return &WriterData->Writer; return nullptr; } ASTDeserializationListener *ASTUnit::getDeserializationListener() { if (WriterData) return &WriterData->Writer; return nullptr; } std::unique_ptr ASTUnit::getBufferForFile(StringRef Filename, std::string *ErrorStr) { assert(FileMgr); auto Buffer = FileMgr->getBufferForFile(Filename); if (Buffer) return std::move(*Buffer); if (ErrorStr) *ErrorStr = Buffer.getError().message(); return nullptr; } /// \brief Configure the diagnostics object for use with ASTUnit. void ASTUnit::ConfigureDiags(IntrusiveRefCntPtr Diags, ASTUnit &AST, bool CaptureDiagnostics) { assert(Diags.get() && "no DiagnosticsEngine was provided"); if (CaptureDiagnostics) Diags->setClient(new StoredDiagnosticConsumer(AST.StoredDiagnostics)); } std::unique_ptr ASTUnit::LoadFromASTFile( const std::string &Filename, const PCHContainerReader &PCHContainerRdr, IntrusiveRefCntPtr Diags, const FileSystemOptions &FileSystemOpts, bool UseDebugInfo, bool OnlyLocalDecls, ArrayRef RemappedFiles, bool CaptureDiagnostics, bool AllowPCHWithCompilerErrors, bool UserFilesAreVolatile) { std::unique_ptr AST(new ASTUnit(true)); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar ASTUnitCleanup(AST.get()); llvm::CrashRecoveryContextCleanupRegistrar > DiagCleanup(Diags.get()); ConfigureDiags(Diags, *AST, CaptureDiagnostics); AST->LangOpts = std::make_shared(); AST->OnlyLocalDecls = OnlyLocalDecls; AST->CaptureDiagnostics = CaptureDiagnostics; AST->Diagnostics = Diags; IntrusiveRefCntPtr VFS = vfs::getRealFileSystem(); AST->FileMgr = new FileManager(FileSystemOpts, VFS); AST->UserFilesAreVolatile = UserFilesAreVolatile; AST->SourceMgr = new SourceManager(AST->getDiagnostics(), AST->getFileManager(), UserFilesAreVolatile); AST->PCMCache = new MemoryBufferCache; AST->HSOpts = std::make_shared(); AST->HSOpts->ModuleFormat = PCHContainerRdr.getFormat(); AST->HeaderInfo.reset(new HeaderSearch(AST->HSOpts, AST->getSourceManager(), AST->getDiagnostics(), AST->getLangOpts(), /*Target=*/nullptr)); AST->PPOpts = std::make_shared(); for (const auto &RemappedFile : RemappedFiles) AST->PPOpts->addRemappedFile(RemappedFile.first, RemappedFile.second); // Gather Info for preprocessor construction later on. HeaderSearch &HeaderInfo = *AST->HeaderInfo; unsigned Counter; AST->PP = std::make_shared( AST->PPOpts, AST->getDiagnostics(), *AST->LangOpts, AST->getSourceManager(), *AST->PCMCache, HeaderInfo, *AST, /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); Preprocessor &PP = *AST->PP; AST->Ctx = new ASTContext(*AST->LangOpts, AST->getSourceManager(), PP.getIdentifierTable(), PP.getSelectorTable(), PP.getBuiltinInfo()); ASTContext &Context = *AST->Ctx; bool disableValid = false; if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION")) disableValid = true; AST->Reader = new ASTReader(PP, Context, PCHContainerRdr, { }, /*isysroot=*/"", /*DisableValidation=*/disableValid, AllowPCHWithCompilerErrors); AST->Reader->setListener(llvm::make_unique( *AST->PP, Context, *AST->HSOpts, *AST->PPOpts, *AST->LangOpts, AST->TargetOpts, AST->Target, Counter)); // Attach the AST reader to the AST context as an external AST // source, so that declarations will be deserialized from the // AST file as needed. // We need the external source to be set up before we read the AST, because // eagerly-deserialized declarations may use it. Context.setExternalSource(AST->Reader); switch (AST->Reader->ReadAST(Filename, serialization::MK_MainFile, SourceLocation(), ASTReader::ARR_None)) { case ASTReader::Success: break; case ASTReader::Failure: case ASTReader::Missing: case ASTReader::OutOfDate: case ASTReader::VersionMismatch: case ASTReader::ConfigurationMismatch: case ASTReader::HadErrors: AST->getDiagnostics().Report(diag::err_fe_unable_to_load_pch); return nullptr; } AST->OriginalSourceFile = AST->Reader->getOriginalSourceFile(); PP.setCounterValue(Counter); // Create an AST consumer, even though it isn't used. AST->Consumer.reset(new ASTConsumer); // Create a semantic analysis object and tell the AST reader about it. AST->TheSema.reset(new Sema(PP, Context, *AST->Consumer)); AST->TheSema->Initialize(); AST->Reader->InitializeSema(*AST->TheSema); // Tell the diagnostic client that we have started a source file. AST->getDiagnostics().getClient()->BeginSourceFile(Context.getLangOpts(),&PP); return AST; } namespace { /// \brief Preprocessor callback class that updates a hash value with the names /// of all macros that have been defined by the translation unit. class MacroDefinitionTrackerPPCallbacks : public PPCallbacks { unsigned &Hash; public: explicit MacroDefinitionTrackerPPCallbacks(unsigned &Hash) : Hash(Hash) { } void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) override { Hash = llvm::HashString(MacroNameTok.getIdentifierInfo()->getName(), Hash); } }; /// \brief Add the given declaration to the hash of all top-level entities. void AddTopLevelDeclarationToHash(Decl *D, unsigned &Hash) { if (!D) return; DeclContext *DC = D->getDeclContext(); if (!DC) return; if (!(DC->isTranslationUnit() || DC->getLookupParent()->isTranslationUnit())) return; if (NamedDecl *ND = dyn_cast(D)) { if (EnumDecl *EnumD = dyn_cast(D)) { // For an unscoped enum include the enumerators in the hash since they // enter the top-level namespace. if (!EnumD->isScoped()) { for (const auto *EI : EnumD->enumerators()) { if (EI->getIdentifier()) Hash = llvm::HashString(EI->getIdentifier()->getName(), Hash); } } } if (ND->getIdentifier()) Hash = llvm::HashString(ND->getIdentifier()->getName(), Hash); else if (DeclarationName Name = ND->getDeclName()) { std::string NameStr = Name.getAsString(); Hash = llvm::HashString(NameStr, Hash); } return; } if (ImportDecl *ImportD = dyn_cast(D)) { if (Module *Mod = ImportD->getImportedModule()) { std::string ModName = Mod->getFullModuleName(); Hash = llvm::HashString(ModName, Hash); } return; } } class TopLevelDeclTrackerConsumer : public ASTConsumer { ASTUnit &Unit; unsigned &Hash; public: TopLevelDeclTrackerConsumer(ASTUnit &_Unit, unsigned &Hash) : Unit(_Unit), Hash(Hash) { Hash = 0; } void handleTopLevelDecl(Decl *D) { if (!D) return; // FIXME: Currently ObjC method declarations are incorrectly being // reported as top-level declarations, even though their DeclContext // is the containing ObjC @interface/@implementation. This is a // fundamental problem in the parser right now. if (isa(D)) return; AddTopLevelDeclarationToHash(D, Hash); Unit.addTopLevelDecl(D); handleFileLevelDecl(D); } void handleFileLevelDecl(Decl *D) { Unit.addFileLevelDecl(D); if (NamespaceDecl *NSD = dyn_cast(D)) { for (auto *I : NSD->decls()) handleFileLevelDecl(I); } } bool HandleTopLevelDecl(DeclGroupRef D) override { for (Decl *TopLevelDecl : D) handleTopLevelDecl(TopLevelDecl); return true; } // We're not interested in "interesting" decls. void HandleInterestingDecl(DeclGroupRef) override {} void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override { for (Decl *TopLevelDecl : D) handleTopLevelDecl(TopLevelDecl); } ASTMutationListener *GetASTMutationListener() override { return Unit.getASTMutationListener(); } ASTDeserializationListener *GetASTDeserializationListener() override { return Unit.getDeserializationListener(); } }; class TopLevelDeclTrackerAction : public ASTFrontendAction { public: ASTUnit &Unit; std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { CI.getPreprocessor().addPPCallbacks( llvm::make_unique( Unit.getCurrentTopLevelHashValue())); return llvm::make_unique( Unit, Unit.getCurrentTopLevelHashValue()); } public: TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {} bool hasCodeCompletionSupport() const override { return false; } TranslationUnitKind getTranslationUnitKind() override { return Unit.getTranslationUnitKind(); } }; class PrecompilePreambleAction : public ASTFrontendAction { ASTUnit &Unit; bool HasEmittedPreamblePCH; public: explicit PrecompilePreambleAction(ASTUnit &Unit) : Unit(Unit), HasEmittedPreamblePCH(false) {} std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override; bool hasEmittedPreamblePCH() const { return HasEmittedPreamblePCH; } void setHasEmittedPreamblePCH() { HasEmittedPreamblePCH = true; } bool shouldEraseOutputFiles() override { return !hasEmittedPreamblePCH(); } bool hasCodeCompletionSupport() const override { return false; } bool hasASTFileSupport() const override { return false; } TranslationUnitKind getTranslationUnitKind() override { return TU_Prefix; } }; class PrecompilePreambleConsumer : public PCHGenerator { ASTUnit &Unit; unsigned &Hash; std::vector TopLevelDecls; PrecompilePreambleAction *Action; std::unique_ptr Out; public: PrecompilePreambleConsumer(ASTUnit &Unit, PrecompilePreambleAction *Action, const Preprocessor &PP, StringRef isysroot, std::unique_ptr Out) : PCHGenerator(PP, "", isysroot, std::make_shared(), ArrayRef>(), /*AllowASTWithErrors=*/true), Unit(Unit), Hash(Unit.getCurrentTopLevelHashValue()), Action(Action), Out(std::move(Out)) { Hash = 0; } bool HandleTopLevelDecl(DeclGroupRef DG) override { for (Decl *D : DG) { // FIXME: Currently ObjC method declarations are incorrectly being // reported as top-level declarations, even though their DeclContext // is the containing ObjC @interface/@implementation. This is a // fundamental problem in the parser right now. if (isa(D)) continue; AddTopLevelDeclarationToHash(D, Hash); TopLevelDecls.push_back(D); } return true; } void HandleTranslationUnit(ASTContext &Ctx) override { PCHGenerator::HandleTranslationUnit(Ctx); if (hasEmittedPCH()) { // Write the generated bitstream to "Out". *Out << getPCH(); // Make sure it hits disk now. Out->flush(); // Free the buffer. llvm::SmallVector Empty; getPCH() = std::move(Empty); // Translate the top-level declarations we captured during // parsing into declaration IDs in the precompiled // preamble. This will allow us to deserialize those top-level // declarations when requested. for (Decl *D : TopLevelDecls) { // Invalid top-level decls may not have been serialized. if (D->isInvalidDecl()) continue; Unit.addTopLevelDeclFromPreamble(getWriter().getDeclID(D)); } Action->setHasEmittedPreamblePCH(); } } }; } // anonymous namespace std::unique_ptr PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { std::string Sysroot; std::string OutputFile; std::unique_ptr OS = GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot, OutputFile); if (!OS) return nullptr; if (!CI.getFrontendOpts().RelocatablePCH) Sysroot.clear(); CI.getPreprocessor().addPPCallbacks( llvm::make_unique( Unit.getCurrentTopLevelHashValue())); return llvm::make_unique( Unit, this, CI.getPreprocessor(), Sysroot, std::move(OS)); } static bool isNonDriverDiag(const StoredDiagnostic &StoredDiag) { return StoredDiag.getLocation().isValid(); } static void checkAndRemoveNonDriverDiags(SmallVectorImpl &StoredDiags) { // Get rid of stored diagnostics except the ones from the driver which do not // have a source location. StoredDiags.erase( std::remove_if(StoredDiags.begin(), StoredDiags.end(), isNonDriverDiag), StoredDiags.end()); } static void checkAndSanitizeDiags(SmallVectorImpl & StoredDiagnostics, SourceManager &SM) { // The stored diagnostic has the old source manager in it; update // the locations to refer into the new source manager. Since we've // been careful to make sure that the source manager's state // before and after are identical, so that we can reuse the source // location itself. for (StoredDiagnostic &SD : StoredDiagnostics) { if (SD.getLocation().isValid()) { FullSourceLoc Loc(SD.getLocation(), SM); SD.setLocation(Loc); } } } /// Parse the source file into a translation unit using the given compiler /// invocation, replacing the current translation unit. /// /// \returns True if a failure occurred that causes the ASTUnit not to /// contain any translation-unit information, false otherwise. bool ASTUnit::Parse(std::shared_ptr PCHContainerOps, std::unique_ptr OverrideMainBuffer, IntrusiveRefCntPtr VFS) { if (!Invocation) return true; // Create the compiler instance to use for building the AST. std::unique_ptr Clang( new CompilerInstance(std::move(PCHContainerOps))); if (FileMgr && VFS) { assert(VFS == FileMgr->getVirtualFileSystem() && "VFS passed to Parse and VFS in FileMgr are different"); } else if (VFS) { Clang->setVirtualFileSystem(VFS); } // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar CICleanup(Clang.get()); Clang->setInvocation(std::make_shared(*Invocation)); OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); // Set up diagnostics, capturing any diagnostics that would // otherwise be dropped. Clang->setDiagnostics(&getDiagnostics()); // Create the target instance. Clang->setTarget(TargetInfo::CreateTargetInfo( Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); if (!Clang->hasTarget()) return true; // Inform the target of the language options. // // FIXME: We shouldn't need to do this, the target should be immutable once // created. This complexity should be lifted elsewhere. Clang->getTarget().adjust(Clang->getLangOpts()); assert(Clang->getFrontendOpts().Inputs.size() == 1 && "Invocation must have exactly one source file!"); assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() == InputKind::Source && "FIXME: AST inputs not yet supported here!"); assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() != InputKind::LLVM_IR && "IR inputs not support here!"); // Configure the various subsystems. LangOpts = Clang->getInvocation().LangOpts; FileSystemOpts = Clang->getFileSystemOpts(); if (!FileMgr) { Clang->createFileManager(); FileMgr = &Clang->getFileManager(); } ResetForParse(); SourceMgr = new SourceManager(getDiagnostics(), *FileMgr, UserFilesAreVolatile); if (!OverrideMainBuffer) { checkAndRemoveNonDriverDiags(StoredDiagnostics); TopLevelDeclsInPreamble.clear(); } // Create a file manager object to provide access to and cache the filesystem. Clang->setFileManager(&getFileManager()); // Create the source manager. Clang->setSourceManager(&getSourceManager()); // If the main file has been overridden due to the use of a preamble, // make that override happen and introduce the preamble. PreprocessorOptions &PreprocessorOpts = Clang->getPreprocessorOpts(); if (OverrideMainBuffer) { PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer.get()); PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); PreprocessorOpts.PrecompiledPreambleBytes.second = PreambleEndsAtStartOfLine; PreprocessorOpts.ImplicitPCHInclude = getPreambleFile(this); PreprocessorOpts.DisablePCHValidation = true; // The stored diagnostic has the old source manager in it; update // the locations to refer into the new source manager. Since we've // been careful to make sure that the source manager's state // before and after are identical, so that we can reuse the source // location itself. checkAndSanitizeDiags(StoredDiagnostics, getSourceManager()); // Keep track of the override buffer; SavedMainFileBuffer = std::move(OverrideMainBuffer); } std::unique_ptr Act( new TopLevelDeclTrackerAction(*this)); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar ActCleanup(Act.get()); if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) goto error; if (SavedMainFileBuffer) TranslateStoredDiagnostics(getFileManager(), getSourceManager(), PreambleDiagnostics, StoredDiagnostics); + else + PreambleSrcLocCache.clear(); if (!Act->Execute()) goto error; transferASTDataFromCompilerInstance(*Clang); Act->EndSourceFile(); FailedParseDiagnostics.clear(); return false; error: // Remove the overridden buffer we used for the preamble. SavedMainFileBuffer = nullptr; // Keep the ownership of the data in the ASTUnit because the client may // want to see the diagnostics. transferASTDataFromCompilerInstance(*Clang); FailedParseDiagnostics.swap(StoredDiagnostics); StoredDiagnostics.clear(); NumStoredDiagnosticsFromDriver = 0; return true; } /// \brief Simple function to retrieve a path for a preamble precompiled header. static std::string GetPreamblePCHPath() { // FIXME: This is a hack so that we can override the preamble file during // crash-recovery testing, which is the only case where the preamble files // are not necessarily cleaned up. const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE"); if (TmpFile) return TmpFile; SmallString<128> Path; llvm::sys::fs::createTemporaryFile("preamble", "pch", Path); return Path.str(); } /// \brief Compute the preamble for the main file, providing the source buffer /// that corresponds to the main file along with a pair (bytes, start-of-line) /// that describes the preamble. ASTUnit::ComputedPreamble ASTUnit::ComputePreamble(CompilerInvocation &Invocation, unsigned MaxLines, IntrusiveRefCntPtr VFS) { FrontendOptions &FrontendOpts = Invocation.getFrontendOpts(); PreprocessorOptions &PreprocessorOpts = Invocation.getPreprocessorOpts(); // Try to determine if the main file has been remapped, either from the // command line (to another file) or directly through the compiler invocation // (to a memory buffer). llvm::MemoryBuffer *Buffer = nullptr; std::unique_ptr BufferOwner; std::string MainFilePath(FrontendOpts.Inputs[0].getFile()); auto MainFileStatus = VFS->status(MainFilePath); if (MainFileStatus) { llvm::sys::fs::UniqueID MainFileID = MainFileStatus->getUniqueID(); // Check whether there is a file-file remapping of the main file for (const auto &RF : PreprocessorOpts.RemappedFiles) { std::string MPath(RF.first); auto MPathStatus = VFS->status(MPath); if (MPathStatus) { llvm::sys::fs::UniqueID MID = MPathStatus->getUniqueID(); if (MainFileID == MID) { // We found a remapping. Try to load the resulting, remapped source. BufferOwner = valueOrNull(VFS->getBufferForFile(RF.second)); if (!BufferOwner) return ComputedPreamble(nullptr, nullptr, 0, true); } } } // Check whether there is a file-buffer remapping. It supercedes the // file-file remapping. for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) { std::string MPath(RB.first); auto MPathStatus = VFS->status(MPath); if (MPathStatus) { llvm::sys::fs::UniqueID MID = MPathStatus->getUniqueID(); if (MainFileID == MID) { // We found a remapping. BufferOwner.reset(); Buffer = const_cast(RB.second); } } } } // If the main source file was not remapped, load it now. if (!Buffer && !BufferOwner) { BufferOwner = valueOrNull(VFS->getBufferForFile(FrontendOpts.Inputs[0].getFile())); if (!BufferOwner) return ComputedPreamble(nullptr, nullptr, 0, true); } if (!Buffer) Buffer = BufferOwner.get(); auto Pre = Lexer::ComputePreamble(Buffer->getBuffer(), *Invocation.getLangOpts(), MaxLines); return ComputedPreamble(Buffer, std::move(BufferOwner), Pre.first, Pre.second); } ASTUnit::PreambleFileHash ASTUnit::PreambleFileHash::createForFile(off_t Size, time_t ModTime) { PreambleFileHash Result; Result.Size = Size; Result.ModTime = ModTime; Result.MD5 = {}; return Result; } ASTUnit::PreambleFileHash ASTUnit::PreambleFileHash::createForMemoryBuffer( const llvm::MemoryBuffer *Buffer) { PreambleFileHash Result; Result.Size = Buffer->getBufferSize(); Result.ModTime = 0; llvm::MD5 MD5Ctx; MD5Ctx.update(Buffer->getBuffer().data()); MD5Ctx.final(Result.MD5); return Result; } namespace clang { bool operator==(const ASTUnit::PreambleFileHash &LHS, const ASTUnit::PreambleFileHash &RHS) { return LHS.Size == RHS.Size && LHS.ModTime == RHS.ModTime && LHS.MD5 == RHS.MD5; } } // namespace clang static std::pair makeStandaloneRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts) { CharSourceRange FileRange = Lexer::makeFileCharRange(Range, SM, LangOpts); unsigned Offset = SM.getFileOffset(FileRange.getBegin()); unsigned EndOffset = SM.getFileOffset(FileRange.getEnd()); return std::make_pair(Offset, EndOffset); } static ASTUnit::StandaloneFixIt makeStandaloneFixIt(const SourceManager &SM, const LangOptions &LangOpts, const FixItHint &InFix) { ASTUnit::StandaloneFixIt OutFix; OutFix.RemoveRange = makeStandaloneRange(InFix.RemoveRange, SM, LangOpts); OutFix.InsertFromRange = makeStandaloneRange(InFix.InsertFromRange, SM, LangOpts); OutFix.CodeToInsert = InFix.CodeToInsert; OutFix.BeforePreviousInsertions = InFix.BeforePreviousInsertions; return OutFix; } static ASTUnit::StandaloneDiagnostic makeStandaloneDiagnostic(const LangOptions &LangOpts, const StoredDiagnostic &InDiag) { ASTUnit::StandaloneDiagnostic OutDiag; OutDiag.ID = InDiag.getID(); OutDiag.Level = InDiag.getLevel(); OutDiag.Message = InDiag.getMessage(); OutDiag.LocOffset = 0; if (InDiag.getLocation().isInvalid()) return OutDiag; const SourceManager &SM = InDiag.getLocation().getManager(); SourceLocation FileLoc = SM.getFileLoc(InDiag.getLocation()); OutDiag.Filename = SM.getFilename(FileLoc); if (OutDiag.Filename.empty()) return OutDiag; OutDiag.LocOffset = SM.getFileOffset(FileLoc); for (const CharSourceRange &Range : InDiag.getRanges()) OutDiag.Ranges.push_back(makeStandaloneRange(Range, SM, LangOpts)); for (const FixItHint &FixIt : InDiag.getFixIts()) OutDiag.FixIts.push_back(makeStandaloneFixIt(SM, LangOpts, FixIt)); return OutDiag; } /// \brief Attempt to build or re-use a precompiled preamble when (re-)parsing /// the source file. /// /// This routine will compute the preamble of the main source file. If a /// non-trivial preamble is found, it will precompile that preamble into a /// precompiled header so that the precompiled preamble can be used to reduce /// reparsing time. If a precompiled preamble has already been constructed, /// this routine will determine if it is still valid and, if so, avoid /// rebuilding the precompiled preamble. /// /// \param AllowRebuild When true (the default), this routine is /// allowed to rebuild the precompiled preamble if it is found to be /// out-of-date. /// /// \param MaxLines When non-zero, the maximum number of lines that /// can occur within the preamble. /// /// \returns If the precompiled preamble can be used, returns a newly-allocated /// buffer that should be used in place of the main file when doing so. /// Otherwise, returns a NULL pointer. std::unique_ptr ASTUnit::getMainBufferWithPrecompiledPreamble( std::shared_ptr PCHContainerOps, const CompilerInvocation &PreambleInvocationIn, IntrusiveRefCntPtr VFS, bool AllowRebuild, unsigned MaxLines) { assert(VFS && "VFS is null"); auto PreambleInvocation = std::make_shared(PreambleInvocationIn); FrontendOptions &FrontendOpts = PreambleInvocation->getFrontendOpts(); PreprocessorOptions &PreprocessorOpts = PreambleInvocation->getPreprocessorOpts(); ComputedPreamble NewPreamble = ComputePreamble(*PreambleInvocation, MaxLines, VFS); if (!NewPreamble.Size) { // We couldn't find a preamble in the main source. Clear out the current // preamble, if we have one. It's obviously no good any more. Preamble.clear(); erasePreambleFile(this); // The next time we actually see a preamble, precompile it. PreambleRebuildCounter = 1; return nullptr; } if (!Preamble.empty()) { // We've previously computed a preamble. Check whether we have the same // preamble now that we did before, and that there's enough space in // the main-file buffer within the precompiled preamble to fit the // new main file. if (Preamble.size() == NewPreamble.Size && PreambleEndsAtStartOfLine == NewPreamble.PreambleEndsAtStartOfLine && memcmp(Preamble.getBufferStart(), NewPreamble.Buffer->getBufferStart(), NewPreamble.Size) == 0) { // The preamble has not changed. We may be able to re-use the precompiled // preamble. // Check that none of the files used by the preamble have changed. bool AnyFileChanged = false; // First, make a record of those files that have been overridden via // remapping or unsaved_files. std::map OverriddenFiles; for (const auto &R : PreprocessorOpts.RemappedFiles) { if (AnyFileChanged) break; vfs::Status Status; if (!moveOnNoError(VFS->status(R.second), Status)) { // If we can't stat the file we're remapping to, assume that something // horrible happened. AnyFileChanged = true; break; } OverriddenFiles[Status.getUniqueID()] = PreambleFileHash::createForFile( Status.getSize(), llvm::sys::toTimeT(Status.getLastModificationTime())); } for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) { if (AnyFileChanged) break; vfs::Status Status; if (!moveOnNoError(VFS->status(RB.first), Status)) { AnyFileChanged = true; break; } OverriddenFiles[Status.getUniqueID()] = PreambleFileHash::createForMemoryBuffer(RB.second); } // Check whether anything has changed. for (llvm::StringMap::iterator F = FilesInPreamble.begin(), FEnd = FilesInPreamble.end(); !AnyFileChanged && F != FEnd; ++F) { vfs::Status Status; if (!moveOnNoError(VFS->status(F->first()), Status)) { // If we can't stat the file, assume that something horrible happened. AnyFileChanged = true; break; } std::map::iterator Overridden = OverriddenFiles.find(Status.getUniqueID()); if (Overridden != OverriddenFiles.end()) { // This file was remapped; check whether the newly-mapped file // matches up with the previous mapping. if (Overridden->second != F->second) AnyFileChanged = true; continue; } // The file was not remapped; check whether it has changed on disk. if (Status.getSize() != uint64_t(F->second.Size) || llvm::sys::toTimeT(Status.getLastModificationTime()) != F->second.ModTime) AnyFileChanged = true; } if (!AnyFileChanged) { // Okay! We can re-use the precompiled preamble. // Set the state of the diagnostic object to mimic its state // after parsing the preamble. getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), PreambleInvocation->getDiagnosticOpts()); getDiagnostics().setNumWarnings(NumWarningsInPreamble); return llvm::MemoryBuffer::getMemBufferCopy( NewPreamble.Buffer->getBuffer(), FrontendOpts.Inputs[0].getFile()); } } // If we aren't allowed to rebuild the precompiled preamble, just // return now. if (!AllowRebuild) return nullptr; // We can't reuse the previously-computed preamble. Build a new one. Preamble.clear(); PreambleDiagnostics.clear(); erasePreambleFile(this); PreambleRebuildCounter = 1; } else if (!AllowRebuild) { // We aren't allowed to rebuild the precompiled preamble; just // return now. return nullptr; } // If the preamble rebuild counter > 1, it's because we previously // failed to build a preamble and we're not yet ready to try // again. Decrement the counter and return a failure. if (PreambleRebuildCounter > 1) { --PreambleRebuildCounter; return nullptr; } // Create a temporary file for the precompiled preamble. In rare // circumstances, this can fail. std::string PreamblePCHPath = GetPreamblePCHPath(); if (PreamblePCHPath.empty()) { // Try again next time. PreambleRebuildCounter = 1; return nullptr; } // We did not previously compute a preamble, or it can't be reused anyway. SimpleTimer PreambleTimer(WantTiming); PreambleTimer.setOutput("Precompiling preamble"); // Save the preamble text for later; we'll need to compare against it for // subsequent reparses. StringRef MainFilename = FrontendOpts.Inputs[0].getFile(); Preamble.assign(FileMgr->getFile(MainFilename), NewPreamble.Buffer->getBufferStart(), NewPreamble.Buffer->getBufferStart() + NewPreamble.Size); PreambleEndsAtStartOfLine = NewPreamble.PreambleEndsAtStartOfLine; PreambleBuffer = llvm::MemoryBuffer::getMemBufferCopy( NewPreamble.Buffer->getBuffer().slice(0, Preamble.size()), MainFilename); // Remap the main source file to the preamble buffer. StringRef MainFilePath = FrontendOpts.Inputs[0].getFile(); PreprocessorOpts.addRemappedFile(MainFilePath, PreambleBuffer.get()); // Tell the compiler invocation to generate a temporary precompiled header. FrontendOpts.ProgramAction = frontend::GeneratePCH; // FIXME: Generate the precompiled header into memory? FrontendOpts.OutputFile = PreamblePCHPath; PreprocessorOpts.PrecompiledPreambleBytes.first = 0; PreprocessorOpts.PrecompiledPreambleBytes.second = false; // Create the compiler instance to use for building the precompiled preamble. std::unique_ptr Clang( new CompilerInstance(std::move(PCHContainerOps))); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar CICleanup(Clang.get()); Clang->setInvocation(std::move(PreambleInvocation)); OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); // Set up diagnostics, capturing all of the diagnostics produced. Clang->setDiagnostics(&getDiagnostics()); // Create the target instance. Clang->setTarget(TargetInfo::CreateTargetInfo( Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); if (!Clang->hasTarget()) { llvm::sys::fs::remove(FrontendOpts.OutputFile); Preamble.clear(); PreambleRebuildCounter = DefaultPreambleRebuildInterval; PreprocessorOpts.RemappedFileBuffers.pop_back(); return nullptr; } // Inform the target of the language options. // // FIXME: We shouldn't need to do this, the target should be immutable once // created. This complexity should be lifted elsewhere. Clang->getTarget().adjust(Clang->getLangOpts()); assert(Clang->getFrontendOpts().Inputs.size() == 1 && "Invocation must have exactly one source file!"); assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() == InputKind::Source && "FIXME: AST inputs not yet supported here!"); assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() != InputKind::LLVM_IR && "IR inputs not support here!"); // Clear out old caches and data. getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), Clang->getDiagnosticOpts()); checkAndRemoveNonDriverDiags(StoredDiagnostics); TopLevelDecls.clear(); TopLevelDeclsInPreamble.clear(); PreambleDiagnostics.clear(); VFS = createVFSFromCompilerInvocation(Clang->getInvocation(), getDiagnostics(), VFS); if (!VFS) return nullptr; // Create a file manager object to provide access to and cache the filesystem. Clang->setFileManager(new FileManager(Clang->getFileSystemOpts(), VFS)); // Create the source manager. Clang->setSourceManager(new SourceManager(getDiagnostics(), Clang->getFileManager())); auto PreambleDepCollector = std::make_shared(); Clang->addDependencyCollector(PreambleDepCollector); std::unique_ptr Act; Act.reset(new PrecompilePreambleAction(*this)); if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { llvm::sys::fs::remove(FrontendOpts.OutputFile); Preamble.clear(); PreambleRebuildCounter = DefaultPreambleRebuildInterval; PreprocessorOpts.RemappedFileBuffers.pop_back(); return nullptr; } Act->Execute(); // Transfer any diagnostics generated when parsing the preamble into the set // of preamble diagnostics. for (stored_diag_iterator I = stored_diag_afterDriver_begin(), E = stored_diag_end(); I != E; ++I) PreambleDiagnostics.push_back( makeStandaloneDiagnostic(Clang->getLangOpts(), *I)); Act->EndSourceFile(); checkAndRemoveNonDriverDiags(StoredDiagnostics); if (!Act->hasEmittedPreamblePCH()) { // The preamble PCH failed (e.g. there was a module loading fatal error), // so no precompiled header was generated. Forget that we even tried. // FIXME: Should we leave a note for ourselves to try again? llvm::sys::fs::remove(FrontendOpts.OutputFile); Preamble.clear(); TopLevelDeclsInPreamble.clear(); PreambleRebuildCounter = DefaultPreambleRebuildInterval; PreprocessorOpts.RemappedFileBuffers.pop_back(); return nullptr; } // Keep track of the preamble we precompiled. setPreambleFile(this, FrontendOpts.OutputFile); NumWarningsInPreamble = getDiagnostics().getNumWarnings(); // Keep track of all of the files that the source manager knows about, // so we can verify whether they have changed or not. FilesInPreamble.clear(); SourceManager &SourceMgr = Clang->getSourceManager(); for (auto &Filename : PreambleDepCollector->getDependencies()) { const FileEntry *File = Clang->getFileManager().getFile(Filename); if (!File || File == SourceMgr.getFileEntryForID(SourceMgr.getMainFileID())) continue; if (time_t ModTime = File->getModificationTime()) { FilesInPreamble[File->getName()] = PreambleFileHash::createForFile( File->getSize(), ModTime); } else { llvm::MemoryBuffer *Buffer = SourceMgr.getMemoryBufferForFile(File); FilesInPreamble[File->getName()] = PreambleFileHash::createForMemoryBuffer(Buffer); } } PreambleRebuildCounter = 1; PreprocessorOpts.RemappedFileBuffers.pop_back(); // If the hash of top-level entities differs from the hash of the top-level // entities the last time we rebuilt the preamble, clear out the completion // cache. if (CurrentTopLevelHashValue != PreambleTopLevelHashValue) { CompletionCacheTopLevelHashValue = 0; PreambleTopLevelHashValue = CurrentTopLevelHashValue; } return llvm::MemoryBuffer::getMemBufferCopy(NewPreamble.Buffer->getBuffer(), MainFilename); } void ASTUnit::RealizeTopLevelDeclsFromPreamble() { std::vector Resolved; Resolved.reserve(TopLevelDeclsInPreamble.size()); ExternalASTSource &Source = *getASTContext().getExternalSource(); for (serialization::DeclID TopLevelDecl : TopLevelDeclsInPreamble) { // Resolve the declaration ID to an actual declaration, possibly // deserializing the declaration in the process. if (Decl *D = Source.GetExternalDecl(TopLevelDecl)) Resolved.push_back(D); } TopLevelDeclsInPreamble.clear(); TopLevelDecls.insert(TopLevelDecls.begin(), Resolved.begin(), Resolved.end()); } void ASTUnit::transferASTDataFromCompilerInstance(CompilerInstance &CI) { // Steal the created target, context, and preprocessor if they have been // created. assert(CI.hasInvocation() && "missing invocation"); LangOpts = CI.getInvocation().LangOpts; TheSema = CI.takeSema(); Consumer = CI.takeASTConsumer(); if (CI.hasASTContext()) Ctx = &CI.getASTContext(); if (CI.hasPreprocessor()) PP = CI.getPreprocessorPtr(); CI.setSourceManager(nullptr); CI.setFileManager(nullptr); if (CI.hasTarget()) Target = &CI.getTarget(); Reader = CI.getModuleManager(); HadModuleLoaderFatalFailure = CI.hadModuleLoaderFatalFailure(); } StringRef ASTUnit::getMainFileName() const { if (Invocation && !Invocation->getFrontendOpts().Inputs.empty()) { const FrontendInputFile &Input = Invocation->getFrontendOpts().Inputs[0]; if (Input.isFile()) return Input.getFile(); else return Input.getBuffer()->getBufferIdentifier(); } if (SourceMgr) { if (const FileEntry * FE = SourceMgr->getFileEntryForID(SourceMgr->getMainFileID())) return FE->getName(); } return StringRef(); } StringRef ASTUnit::getASTFileName() const { if (!isMainFileAST()) return StringRef(); serialization::ModuleFile & Mod = Reader->getModuleManager().getPrimaryModule(); return Mod.FileName; } std::unique_ptr ASTUnit::create(std::shared_ptr CI, IntrusiveRefCntPtr Diags, bool CaptureDiagnostics, bool UserFilesAreVolatile) { std::unique_ptr AST(new ASTUnit(false)); ConfigureDiags(Diags, *AST, CaptureDiagnostics); IntrusiveRefCntPtr VFS = createVFSFromCompilerInvocation(*CI, *Diags); if (!VFS) return nullptr; AST->Diagnostics = Diags; AST->FileSystemOpts = CI->getFileSystemOpts(); AST->Invocation = std::move(CI); AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS); AST->UserFilesAreVolatile = UserFilesAreVolatile; AST->SourceMgr = new SourceManager(AST->getDiagnostics(), *AST->FileMgr, UserFilesAreVolatile); AST->PCMCache = new MemoryBufferCache; return AST; } ASTUnit *ASTUnit::LoadFromCompilerInvocationAction( std::shared_ptr CI, std::shared_ptr PCHContainerOps, IntrusiveRefCntPtr Diags, FrontendAction *Action, ASTUnit *Unit, bool Persistent, StringRef ResourceFilesPath, bool OnlyLocalDecls, bool CaptureDiagnostics, unsigned PrecompilePreambleAfterNParses, bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, bool UserFilesAreVolatile, std::unique_ptr *ErrAST) { assert(CI && "A CompilerInvocation is required"); std::unique_ptr OwnAST; ASTUnit *AST = Unit; if (!AST) { // Create the AST unit. OwnAST = create(CI, Diags, CaptureDiagnostics, UserFilesAreVolatile); AST = OwnAST.get(); if (!AST) return nullptr; } if (!ResourceFilesPath.empty()) { // Override the resources path. CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; } AST->OnlyLocalDecls = OnlyLocalDecls; AST->CaptureDiagnostics = CaptureDiagnostics; if (PrecompilePreambleAfterNParses > 0) AST->PreambleRebuildCounter = PrecompilePreambleAfterNParses; AST->TUKind = Action ? Action->getTranslationUnitKind() : TU_Complete; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; AST->IncludeBriefCommentsInCodeCompletion = IncludeBriefCommentsInCodeCompletion; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar ASTUnitCleanup(OwnAST.get()); llvm::CrashRecoveryContextCleanupRegistrar > DiagCleanup(Diags.get()); // We'll manage file buffers ourselves. CI->getPreprocessorOpts().RetainRemappedFileBuffers = true; CI->getFrontendOpts().DisableFree = false; ProcessWarningOptions(AST->getDiagnostics(), CI->getDiagnosticOpts()); // Create the compiler instance to use for building the AST. std::unique_ptr Clang( new CompilerInstance(std::move(PCHContainerOps))); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar CICleanup(Clang.get()); Clang->setInvocation(std::move(CI)); AST->OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); // Set up diagnostics, capturing any diagnostics that would // otherwise be dropped. Clang->setDiagnostics(&AST->getDiagnostics()); // Create the target instance. Clang->setTarget(TargetInfo::CreateTargetInfo( Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); if (!Clang->hasTarget()) return nullptr; // Inform the target of the language options. // // FIXME: We shouldn't need to do this, the target should be immutable once // created. This complexity should be lifted elsewhere. Clang->getTarget().adjust(Clang->getLangOpts()); assert(Clang->getFrontendOpts().Inputs.size() == 1 && "Invocation must have exactly one source file!"); assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() == InputKind::Source && "FIXME: AST inputs not yet supported here!"); assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() != InputKind::LLVM_IR && "IR inputs not support here!"); // Configure the various subsystems. AST->TheSema.reset(); AST->Ctx = nullptr; AST->PP = nullptr; AST->Reader = nullptr; // Create a file manager object to provide access to and cache the filesystem. Clang->setFileManager(&AST->getFileManager()); // Create the source manager. Clang->setSourceManager(&AST->getSourceManager()); FrontendAction *Act = Action; std::unique_ptr TrackerAct; if (!Act) { TrackerAct.reset(new TopLevelDeclTrackerAction(*AST)); Act = TrackerAct.get(); } // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar ActCleanup(TrackerAct.get()); if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { AST->transferASTDataFromCompilerInstance(*Clang); if (OwnAST && ErrAST) ErrAST->swap(OwnAST); return nullptr; } if (Persistent && !TrackerAct) { Clang->getPreprocessor().addPPCallbacks( llvm::make_unique( AST->getCurrentTopLevelHashValue())); std::vector> Consumers; if (Clang->hasASTConsumer()) Consumers.push_back(Clang->takeASTConsumer()); Consumers.push_back(llvm::make_unique( *AST, AST->getCurrentTopLevelHashValue())); Clang->setASTConsumer( llvm::make_unique(std::move(Consumers))); } if (!Act->Execute()) { AST->transferASTDataFromCompilerInstance(*Clang); if (OwnAST && ErrAST) ErrAST->swap(OwnAST); return nullptr; } // Steal the created target, context, and preprocessor. AST->transferASTDataFromCompilerInstance(*Clang); Act->EndSourceFile(); if (OwnAST) return OwnAST.release(); else return AST; } bool ASTUnit::LoadFromCompilerInvocation( std::shared_ptr PCHContainerOps, unsigned PrecompilePreambleAfterNParses, IntrusiveRefCntPtr VFS) { if (!Invocation) return true; assert(VFS && "VFS is null"); // We'll manage file buffers ourselves. Invocation->getPreprocessorOpts().RetainRemappedFileBuffers = true; Invocation->getFrontendOpts().DisableFree = false; getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); std::unique_ptr OverrideMainBuffer; if (PrecompilePreambleAfterNParses > 0) { PreambleRebuildCounter = PrecompilePreambleAfterNParses; OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS); getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); } SimpleTimer ParsingTimer(WantTiming); ParsingTimer.setOutput("Parsing " + getMainFileName()); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar MemBufferCleanup(OverrideMainBuffer.get()); return Parse(std::move(PCHContainerOps), std::move(OverrideMainBuffer), VFS); } std::unique_ptr ASTUnit::LoadFromCompilerInvocation( std::shared_ptr CI, std::shared_ptr PCHContainerOps, IntrusiveRefCntPtr Diags, FileManager *FileMgr, bool OnlyLocalDecls, bool CaptureDiagnostics, unsigned PrecompilePreambleAfterNParses, TranslationUnitKind TUKind, bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, bool UserFilesAreVolatile) { // Create the AST unit. std::unique_ptr AST(new ASTUnit(false)); ConfigureDiags(Diags, *AST, CaptureDiagnostics); AST->Diagnostics = Diags; AST->OnlyLocalDecls = OnlyLocalDecls; AST->CaptureDiagnostics = CaptureDiagnostics; AST->TUKind = TUKind; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; AST->IncludeBriefCommentsInCodeCompletion = IncludeBriefCommentsInCodeCompletion; AST->Invocation = std::move(CI); AST->FileSystemOpts = FileMgr->getFileSystemOpts(); AST->FileMgr = FileMgr; AST->UserFilesAreVolatile = UserFilesAreVolatile; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar ASTUnitCleanup(AST.get()); llvm::CrashRecoveryContextCleanupRegistrar > DiagCleanup(Diags.get()); if (AST->LoadFromCompilerInvocation(std::move(PCHContainerOps), PrecompilePreambleAfterNParses, AST->FileMgr->getVirtualFileSystem())) return nullptr; return AST; } ASTUnit *ASTUnit::LoadFromCommandLine( const char **ArgBegin, const char **ArgEnd, std::shared_ptr PCHContainerOps, IntrusiveRefCntPtr Diags, StringRef ResourceFilesPath, bool OnlyLocalDecls, bool CaptureDiagnostics, ArrayRef RemappedFiles, bool RemappedFilesKeepOriginalName, unsigned PrecompilePreambleAfterNParses, TranslationUnitKind TUKind, bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, bool AllowPCHWithCompilerErrors, bool SkipFunctionBodies, bool SingleFileParse, bool UserFilesAreVolatile, bool ForSerialization, llvm::Optional ModuleFormat, std::unique_ptr *ErrAST, IntrusiveRefCntPtr VFS) { assert(Diags.get() && "no DiagnosticsEngine was provided"); SmallVector StoredDiagnostics; std::shared_ptr CI; { CaptureDroppedDiagnostics Capture(CaptureDiagnostics, *Diags, StoredDiagnostics); CI = clang::createInvocationFromCommandLine( llvm::makeArrayRef(ArgBegin, ArgEnd), Diags); if (!CI) return nullptr; } // Override any files that need remapping for (const auto &RemappedFile : RemappedFiles) { CI->getPreprocessorOpts().addRemappedFile(RemappedFile.first, RemappedFile.second); } PreprocessorOptions &PPOpts = CI->getPreprocessorOpts(); PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName; PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors; PPOpts.GeneratePreamble = PrecompilePreambleAfterNParses != 0; PPOpts.SingleFileParseMode = SingleFileParse; // Override the resources path. CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; CI->getFrontendOpts().SkipFunctionBodies = SkipFunctionBodies; if (ModuleFormat) CI->getHeaderSearchOpts().ModuleFormat = ModuleFormat.getValue(); // Create the AST unit. std::unique_ptr AST; AST.reset(new ASTUnit(false)); ConfigureDiags(Diags, *AST, CaptureDiagnostics); AST->Diagnostics = Diags; AST->FileSystemOpts = CI->getFileSystemOpts(); if (!VFS) VFS = vfs::getRealFileSystem(); VFS = createVFSFromCompilerInvocation(*CI, *Diags, VFS); if (!VFS) return nullptr; AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS); AST->PCMCache = new MemoryBufferCache; AST->OnlyLocalDecls = OnlyLocalDecls; AST->CaptureDiagnostics = CaptureDiagnostics; AST->TUKind = TUKind; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; AST->IncludeBriefCommentsInCodeCompletion = IncludeBriefCommentsInCodeCompletion; AST->UserFilesAreVolatile = UserFilesAreVolatile; AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size(); AST->StoredDiagnostics.swap(StoredDiagnostics); AST->Invocation = CI; if (ForSerialization) AST->WriterData.reset(new ASTWriterData(*AST->PCMCache)); // Zero out now to ease cleanup during crash recovery. CI = nullptr; Diags = nullptr; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar ASTUnitCleanup(AST.get()); if (AST->LoadFromCompilerInvocation(std::move(PCHContainerOps), PrecompilePreambleAfterNParses, VFS)) { // Some error occurred, if caller wants to examine diagnostics, pass it the // ASTUnit. if (ErrAST) { AST->StoredDiagnostics.swap(AST->FailedParseDiagnostics); ErrAST->swap(AST); } return nullptr; } return AST.release(); } bool ASTUnit::Reparse(std::shared_ptr PCHContainerOps, ArrayRef RemappedFiles, IntrusiveRefCntPtr VFS) { if (!Invocation) return true; if (!VFS) { assert(FileMgr && "FileMgr is null on Reparse call"); VFS = FileMgr->getVirtualFileSystem(); } clearFileLevelDecls(); SimpleTimer ParsingTimer(WantTiming); ParsingTimer.setOutput("Reparsing " + getMainFileName()); // Remap files. PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); for (const auto &RB : PPOpts.RemappedFileBuffers) delete RB.second; Invocation->getPreprocessorOpts().clearRemappedFiles(); for (const auto &RemappedFile : RemappedFiles) { Invocation->getPreprocessorOpts().addRemappedFile(RemappedFile.first, RemappedFile.second); } // If we have a preamble file lying around, or if we might try to // build a precompiled preamble, do so now. std::unique_ptr OverrideMainBuffer; if (!getPreambleFile(this).empty() || PreambleRebuildCounter > 0) OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS); // Clear out the diagnostics state. FileMgr.reset(); getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); if (OverrideMainBuffer) getDiagnostics().setNumWarnings(NumWarningsInPreamble); // Parse the sources bool Result = Parse(std::move(PCHContainerOps), std::move(OverrideMainBuffer), VFS); // If we're caching global code-completion results, and the top-level // declarations have changed, clear out the code-completion cache. if (!Result && ShouldCacheCodeCompletionResults && CurrentTopLevelHashValue != CompletionCacheTopLevelHashValue) CacheCodeCompletionResults(); // We now need to clear out the completion info related to this translation // unit; it'll be recreated if necessary. CCTUInfo.reset(); return Result; } void ASTUnit::ResetForParse() { SavedMainFileBuffer.reset(); SourceMgr.reset(); TheSema.reset(); Ctx.reset(); PP.reset(); Reader.reset(); TopLevelDecls.clear(); clearFileLevelDecls(); } //----------------------------------------------------------------------------// // Code completion //----------------------------------------------------------------------------// namespace { /// \brief Code completion consumer that combines the cached code-completion /// results from an ASTUnit with the code-completion results provided to it, /// then passes the result on to class AugmentedCodeCompleteConsumer : public CodeCompleteConsumer { uint64_t NormalContexts; ASTUnit &AST; CodeCompleteConsumer &Next; public: AugmentedCodeCompleteConsumer(ASTUnit &AST, CodeCompleteConsumer &Next, const CodeCompleteOptions &CodeCompleteOpts) : CodeCompleteConsumer(CodeCompleteOpts, Next.isOutputBinary()), AST(AST), Next(Next) { // Compute the set of contexts in which we will look when we don't have // any information about the specific context. NormalContexts = (1LL << CodeCompletionContext::CCC_TopLevel) | (1LL << CodeCompletionContext::CCC_ObjCInterface) | (1LL << CodeCompletionContext::CCC_ObjCImplementation) | (1LL << CodeCompletionContext::CCC_ObjCIvarList) | (1LL << CodeCompletionContext::CCC_Statement) | (1LL << CodeCompletionContext::CCC_Expression) | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) | (1LL << CodeCompletionContext::CCC_DotMemberAccess) | (1LL << CodeCompletionContext::CCC_ArrowMemberAccess) | (1LL << CodeCompletionContext::CCC_ObjCPropertyAccess) | (1LL << CodeCompletionContext::CCC_ObjCProtocolName) | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) | (1LL << CodeCompletionContext::CCC_Recovery); if (AST.getASTContext().getLangOpts().CPlusPlus) NormalContexts |= (1LL << CodeCompletionContext::CCC_EnumTag) | (1LL << CodeCompletionContext::CCC_UnionTag) | (1LL << CodeCompletionContext::CCC_ClassOrStructTag); } void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, CodeCompletionResult *Results, unsigned NumResults) override; void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, OverloadCandidate *Candidates, unsigned NumCandidates) override { Next.ProcessOverloadCandidates(S, CurrentArg, Candidates, NumCandidates); } CodeCompletionAllocator &getAllocator() override { return Next.getAllocator(); } CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return Next.getCodeCompletionTUInfo(); } }; } // anonymous namespace /// \brief Helper function that computes which global names are hidden by the /// local code-completion results. static void CalculateHiddenNames(const CodeCompletionContext &Context, CodeCompletionResult *Results, unsigned NumResults, ASTContext &Ctx, llvm::StringSet &HiddenNames){ bool OnlyTagNames = false; switch (Context.getKind()) { case CodeCompletionContext::CCC_Recovery: case CodeCompletionContext::CCC_TopLevel: case CodeCompletionContext::CCC_ObjCInterface: case CodeCompletionContext::CCC_ObjCImplementation: case CodeCompletionContext::CCC_ObjCIvarList: case CodeCompletionContext::CCC_ClassStructUnion: case CodeCompletionContext::CCC_Statement: case CodeCompletionContext::CCC_Expression: case CodeCompletionContext::CCC_ObjCMessageReceiver: case CodeCompletionContext::CCC_DotMemberAccess: case CodeCompletionContext::CCC_ArrowMemberAccess: case CodeCompletionContext::CCC_ObjCPropertyAccess: case CodeCompletionContext::CCC_Namespace: case CodeCompletionContext::CCC_Type: case CodeCompletionContext::CCC_Name: case CodeCompletionContext::CCC_PotentiallyQualifiedName: case CodeCompletionContext::CCC_ParenthesizedExpression: case CodeCompletionContext::CCC_ObjCInterfaceName: break; case CodeCompletionContext::CCC_EnumTag: case CodeCompletionContext::CCC_UnionTag: case CodeCompletionContext::CCC_ClassOrStructTag: OnlyTagNames = true; break; case CodeCompletionContext::CCC_ObjCProtocolName: case CodeCompletionContext::CCC_MacroName: case CodeCompletionContext::CCC_MacroNameUse: case CodeCompletionContext::CCC_PreprocessorExpression: case CodeCompletionContext::CCC_PreprocessorDirective: case CodeCompletionContext::CCC_NaturalLanguage: case CodeCompletionContext::CCC_SelectorName: case CodeCompletionContext::CCC_TypeQualifiers: case CodeCompletionContext::CCC_Other: case CodeCompletionContext::CCC_OtherWithMacros: case CodeCompletionContext::CCC_ObjCInstanceMessage: case CodeCompletionContext::CCC_ObjCClassMessage: case CodeCompletionContext::CCC_ObjCCategoryName: // We're looking for nothing, or we're looking for names that cannot // be hidden. return; } typedef CodeCompletionResult Result; for (unsigned I = 0; I != NumResults; ++I) { if (Results[I].Kind != Result::RK_Declaration) continue; unsigned IDNS = Results[I].Declaration->getUnderlyingDecl()->getIdentifierNamespace(); bool Hiding = false; if (OnlyTagNames) Hiding = (IDNS & Decl::IDNS_Tag); else { unsigned HiddenIDNS = (Decl::IDNS_Type | Decl::IDNS_Member | Decl::IDNS_Namespace | Decl::IDNS_Ordinary | Decl::IDNS_NonMemberOperator); if (Ctx.getLangOpts().CPlusPlus) HiddenIDNS |= Decl::IDNS_Tag; Hiding = (IDNS & HiddenIDNS); } if (!Hiding) continue; DeclarationName Name = Results[I].Declaration->getDeclName(); if (IdentifierInfo *Identifier = Name.getAsIdentifierInfo()) HiddenNames.insert(Identifier->getName()); else HiddenNames.insert(Name.getAsString()); } } void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, CodeCompletionResult *Results, unsigned NumResults) { // Merge the results we were given with the results we cached. bool AddedResult = false; uint64_t InContexts = Context.getKind() == CodeCompletionContext::CCC_Recovery ? NormalContexts : (1LL << Context.getKind()); // Contains the set of names that are hidden by "local" completion results. llvm::StringSet HiddenNames; typedef CodeCompletionResult Result; SmallVector AllResults; for (ASTUnit::cached_completion_iterator C = AST.cached_completion_begin(), CEnd = AST.cached_completion_end(); C != CEnd; ++C) { // If the context we are in matches any of the contexts we are // interested in, we'll add this result. if ((C->ShowInContexts & InContexts) == 0) continue; // If we haven't added any results previously, do so now. if (!AddedResult) { CalculateHiddenNames(Context, Results, NumResults, S.Context, HiddenNames); AllResults.insert(AllResults.end(), Results, Results + NumResults); AddedResult = true; } // Determine whether this global completion result is hidden by a local // completion result. If so, skip it. if (C->Kind != CXCursor_MacroDefinition && HiddenNames.count(C->Completion->getTypedText())) continue; // Adjust priority based on similar type classes. unsigned Priority = C->Priority; CodeCompletionString *Completion = C->Completion; if (!Context.getPreferredType().isNull()) { if (C->Kind == CXCursor_MacroDefinition) { Priority = getMacroUsagePriority(C->Completion->getTypedText(), S.getLangOpts(), Context.getPreferredType()->isAnyPointerType()); } else if (C->Type) { CanQualType Expected = S.Context.getCanonicalType( Context.getPreferredType().getUnqualifiedType()); SimplifiedTypeClass ExpectedSTC = getSimplifiedTypeClass(Expected); if (ExpectedSTC == C->TypeClass) { // We know this type is similar; check for an exact match. llvm::StringMap &CachedCompletionTypes = AST.getCachedCompletionTypes(); llvm::StringMap::iterator Pos = CachedCompletionTypes.find(QualType(Expected).getAsString()); if (Pos != CachedCompletionTypes.end() && Pos->second == C->Type) Priority /= CCF_ExactTypeMatch; else Priority /= CCF_SimilarTypeMatch; } } } // Adjust the completion string, if required. if (C->Kind == CXCursor_MacroDefinition && Context.getKind() == CodeCompletionContext::CCC_MacroNameUse) { // Create a new code-completion string that just contains the // macro name, without its arguments. CodeCompletionBuilder Builder(getAllocator(), getCodeCompletionTUInfo(), CCP_CodePattern, C->Availability); Builder.AddTypedTextChunk(C->Completion->getTypedText()); Priority = CCP_CodePattern; Completion = Builder.TakeString(); } AllResults.push_back(Result(Completion, Priority, C->Kind, C->Availability)); } // If we did not add any cached completion results, just forward the // results we were given to the next consumer. if (!AddedResult) { Next.ProcessCodeCompleteResults(S, Context, Results, NumResults); return; } Next.ProcessCodeCompleteResults(S, Context, AllResults.data(), AllResults.size()); } void ASTUnit::CodeComplete( StringRef File, unsigned Line, unsigned Column, ArrayRef RemappedFiles, bool IncludeMacros, bool IncludeCodePatterns, bool IncludeBriefComments, CodeCompleteConsumer &Consumer, std::shared_ptr PCHContainerOps, DiagnosticsEngine &Diag, LangOptions &LangOpts, SourceManager &SourceMgr, FileManager &FileMgr, SmallVectorImpl &StoredDiagnostics, SmallVectorImpl &OwnedBuffers) { if (!Invocation) return; SimpleTimer CompletionTimer(WantTiming); CompletionTimer.setOutput("Code completion @ " + File + ":" + Twine(Line) + ":" + Twine(Column)); auto CCInvocation = std::make_shared(*Invocation); FrontendOptions &FrontendOpts = CCInvocation->getFrontendOpts(); CodeCompleteOptions &CodeCompleteOpts = FrontendOpts.CodeCompleteOpts; PreprocessorOptions &PreprocessorOpts = CCInvocation->getPreprocessorOpts(); CodeCompleteOpts.IncludeMacros = IncludeMacros && CachedCompletionResults.empty(); CodeCompleteOpts.IncludeCodePatterns = IncludeCodePatterns; CodeCompleteOpts.IncludeGlobals = CachedCompletionResults.empty(); CodeCompleteOpts.IncludeBriefComments = IncludeBriefComments; assert(IncludeBriefComments == this->IncludeBriefCommentsInCodeCompletion); FrontendOpts.CodeCompletionAt.FileName = File; FrontendOpts.CodeCompletionAt.Line = Line; FrontendOpts.CodeCompletionAt.Column = Column; // Set the language options appropriately. LangOpts = *CCInvocation->getLangOpts(); // Spell-checking and warnings are wasteful during code-completion. LangOpts.SpellChecking = false; CCInvocation->getDiagnosticOpts().IgnoreWarnings = true; std::unique_ptr Clang( new CompilerInstance(PCHContainerOps)); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar CICleanup(Clang.get()); auto &Inv = *CCInvocation; Clang->setInvocation(std::move(CCInvocation)); OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); // Set up diagnostics, capturing any diagnostics produced. Clang->setDiagnostics(&Diag); CaptureDroppedDiagnostics Capture(true, Clang->getDiagnostics(), StoredDiagnostics); ProcessWarningOptions(Diag, Inv.getDiagnosticOpts()); // Create the target instance. Clang->setTarget(TargetInfo::CreateTargetInfo( Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); if (!Clang->hasTarget()) { Clang->setInvocation(nullptr); return; } // Inform the target of the language options. // // FIXME: We shouldn't need to do this, the target should be immutable once // created. This complexity should be lifted elsewhere. Clang->getTarget().adjust(Clang->getLangOpts()); assert(Clang->getFrontendOpts().Inputs.size() == 1 && "Invocation must have exactly one source file!"); assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() == InputKind::Source && "FIXME: AST inputs not yet supported here!"); assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() != InputKind::LLVM_IR && "IR inputs not support here!"); // Use the source and file managers that we were given. Clang->setFileManager(&FileMgr); Clang->setSourceManager(&SourceMgr); // Remap files. PreprocessorOpts.clearRemappedFiles(); PreprocessorOpts.RetainRemappedFileBuffers = true; for (const auto &RemappedFile : RemappedFiles) { PreprocessorOpts.addRemappedFile(RemappedFile.first, RemappedFile.second); OwnedBuffers.push_back(RemappedFile.second); } // Use the code completion consumer we were given, but adding any cached // code-completion results. AugmentedCodeCompleteConsumer *AugmentedConsumer = new AugmentedCodeCompleteConsumer(*this, Consumer, CodeCompleteOpts); Clang->setCodeCompletionConsumer(AugmentedConsumer); // If we have a precompiled preamble, try to use it. We only allow // the use of the precompiled preamble if we're if the completion // point is within the main file, after the end of the precompiled // preamble. std::unique_ptr OverrideMainBuffer; if (!getPreambleFile(this).empty()) { std::string CompleteFilePath(File); auto VFS = FileMgr.getVirtualFileSystem(); auto CompleteFileStatus = VFS->status(CompleteFilePath); if (CompleteFileStatus) { llvm::sys::fs::UniqueID CompleteFileID = CompleteFileStatus->getUniqueID(); std::string MainPath(OriginalSourceFile); auto MainStatus = VFS->status(MainPath); if (MainStatus) { llvm::sys::fs::UniqueID MainID = MainStatus->getUniqueID(); if (CompleteFileID == MainID && Line > 1) OverrideMainBuffer = getMainBufferWithPrecompiledPreamble( PCHContainerOps, Inv, VFS, false, Line - 1); } } } // If the main file has been overridden due to the use of a preamble, // make that override happen and introduce the preamble. if (OverrideMainBuffer) { PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer.get()); PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); PreprocessorOpts.PrecompiledPreambleBytes.second = PreambleEndsAtStartOfLine; PreprocessorOpts.ImplicitPCHInclude = getPreambleFile(this); PreprocessorOpts.DisablePCHValidation = true; OwnedBuffers.push_back(OverrideMainBuffer.release()); } else { PreprocessorOpts.PrecompiledPreambleBytes.first = 0; PreprocessorOpts.PrecompiledPreambleBytes.second = false; } // Disable the preprocessing record if modules are not enabled. if (!Clang->getLangOpts().Modules) PreprocessorOpts.DetailedRecord = false; std::unique_ptr Act; Act.reset(new SyntaxOnlyAction); if (Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { Act->Execute(); Act->EndSourceFile(); } } bool ASTUnit::Save(StringRef File) { if (HadModuleLoaderFatalFailure) return true; // Write to a temporary file and later rename it to the actual file, to avoid // possible race conditions. SmallString<128> TempPath; TempPath = File; TempPath += "-%%%%%%%%"; int fd; if (llvm::sys::fs::createUniqueFile(TempPath, fd, TempPath)) return true; // FIXME: Can we somehow regenerate the stat cache here, or do we need to // unconditionally create a stat cache when we parse the file? llvm::raw_fd_ostream Out(fd, /*shouldClose=*/true); serialize(Out); Out.close(); if (Out.has_error()) { Out.clear_error(); return true; } if (llvm::sys::fs::rename(TempPath, File)) { llvm::sys::fs::remove(TempPath); return true; } return false; } static bool serializeUnit(ASTWriter &Writer, SmallVectorImpl &Buffer, Sema &S, bool hasErrors, raw_ostream &OS) { Writer.WriteAST(S, std::string(), nullptr, "", hasErrors); // Write the generated bitstream to "Out". if (!Buffer.empty()) OS.write(Buffer.data(), Buffer.size()); return false; } bool ASTUnit::serialize(raw_ostream &OS) { // For serialization we are lenient if the errors were only warn-as-error kind. bool hasErrors = getDiagnostics().hasUncompilableErrorOccurred(); if (WriterData) return serializeUnit(WriterData->Writer, WriterData->Buffer, getSema(), hasErrors, OS); SmallString<128> Buffer; llvm::BitstreamWriter Stream(Buffer); MemoryBufferCache PCMCache; ASTWriter Writer(Stream, Buffer, PCMCache, {}); return serializeUnit(Writer, Buffer, getSema(), hasErrors, OS); } typedef ContinuousRangeMap SLocRemap; void ASTUnit::TranslateStoredDiagnostics( FileManager &FileMgr, SourceManager &SrcMgr, const SmallVectorImpl &Diags, SmallVectorImpl &Out) { // Map the standalone diagnostic into the new source manager. We also need to // remap all the locations to the new view. This includes the diag location, // any associated source ranges, and the source ranges of associated fix-its. // FIXME: There should be a cleaner way to do this. - SmallVector Result; Result.reserve(Diags.size()); - const FileEntry *PreviousFE = nullptr; - FileID FID; + for (const StandaloneDiagnostic &SD : Diags) { // Rebuild the StoredDiagnostic. if (SD.Filename.empty()) continue; const FileEntry *FE = FileMgr.getFile(SD.Filename); if (!FE) continue; - if (FE != PreviousFE) { - FID = SrcMgr.translateFile(FE); - PreviousFE = FE; + SourceLocation FileLoc; + auto ItFileID = PreambleSrcLocCache.find(SD.Filename); + if (ItFileID == PreambleSrcLocCache.end()) { + FileID FID = SrcMgr.translateFile(FE); + FileLoc = SrcMgr.getLocForStartOfFile(FID); + PreambleSrcLocCache[SD.Filename] = FileLoc; + } else { + FileLoc = ItFileID->getValue(); } - SourceLocation FileLoc = SrcMgr.getLocForStartOfFile(FID); + if (FileLoc.isInvalid()) continue; SourceLocation L = FileLoc.getLocWithOffset(SD.LocOffset); FullSourceLoc Loc(L, SrcMgr); SmallVector Ranges; Ranges.reserve(SD.Ranges.size()); for (const auto &Range : SD.Ranges) { SourceLocation BL = FileLoc.getLocWithOffset(Range.first); SourceLocation EL = FileLoc.getLocWithOffset(Range.second); Ranges.push_back(CharSourceRange::getCharRange(BL, EL)); } SmallVector FixIts; FixIts.reserve(SD.FixIts.size()); for (const StandaloneFixIt &FixIt : SD.FixIts) { FixIts.push_back(FixItHint()); FixItHint &FH = FixIts.back(); FH.CodeToInsert = FixIt.CodeToInsert; SourceLocation BL = FileLoc.getLocWithOffset(FixIt.RemoveRange.first); SourceLocation EL = FileLoc.getLocWithOffset(FixIt.RemoveRange.second); FH.RemoveRange = CharSourceRange::getCharRange(BL, EL); } Result.push_back(StoredDiagnostic(SD.Level, SD.ID, SD.Message, Loc, Ranges, FixIts)); } Result.swap(Out); } void ASTUnit::addFileLevelDecl(Decl *D) { assert(D); // We only care about local declarations. if (D->isFromASTFile()) return; SourceManager &SM = *SourceMgr; SourceLocation Loc = D->getLocation(); if (Loc.isInvalid() || !SM.isLocalSourceLocation(Loc)) return; // We only keep track of the file-level declarations of each file. if (!D->getLexicalDeclContext()->isFileContext()) return; SourceLocation FileLoc = SM.getFileLoc(Loc); assert(SM.isLocalSourceLocation(FileLoc)); FileID FID; unsigned Offset; std::tie(FID, Offset) = SM.getDecomposedLoc(FileLoc); if (FID.isInvalid()) return; LocDeclsTy *&Decls = FileDecls[FID]; if (!Decls) Decls = new LocDeclsTy(); std::pair LocDecl(Offset, D); if (Decls->empty() || Decls->back().first <= Offset) { Decls->push_back(LocDecl); return; } LocDeclsTy::iterator I = std::upper_bound(Decls->begin(), Decls->end(), LocDecl, llvm::less_first()); Decls->insert(I, LocDecl); } void ASTUnit::findFileRegionDecls(FileID File, unsigned Offset, unsigned Length, SmallVectorImpl &Decls) { if (File.isInvalid()) return; if (SourceMgr->isLoadedFileID(File)) { assert(Ctx->getExternalSource() && "No external source!"); return Ctx->getExternalSource()->FindFileRegionDecls(File, Offset, Length, Decls); } FileDeclsTy::iterator I = FileDecls.find(File); if (I == FileDecls.end()) return; LocDeclsTy &LocDecls = *I->second; if (LocDecls.empty()) return; LocDeclsTy::iterator BeginIt = std::lower_bound(LocDecls.begin(), LocDecls.end(), std::make_pair(Offset, (Decl *)nullptr), llvm::less_first()); if (BeginIt != LocDecls.begin()) --BeginIt; // If we are pointing at a top-level decl inside an objc container, we need // to backtrack until we find it otherwise we will fail to report that the // region overlaps with an objc container. while (BeginIt != LocDecls.begin() && BeginIt->second->isTopLevelDeclInObjCContainer()) --BeginIt; LocDeclsTy::iterator EndIt = std::upper_bound( LocDecls.begin(), LocDecls.end(), std::make_pair(Offset + Length, (Decl *)nullptr), llvm::less_first()); if (EndIt != LocDecls.end()) ++EndIt; for (LocDeclsTy::iterator DIt = BeginIt; DIt != EndIt; ++DIt) Decls.push_back(DIt->second); } SourceLocation ASTUnit::getLocation(const FileEntry *File, unsigned Line, unsigned Col) const { const SourceManager &SM = getSourceManager(); SourceLocation Loc = SM.translateFileLineCol(File, Line, Col); return SM.getMacroArgExpandedLocation(Loc); } SourceLocation ASTUnit::getLocation(const FileEntry *File, unsigned Offset) const { const SourceManager &SM = getSourceManager(); SourceLocation FileLoc = SM.translateFileLineCol(File, 1, 1); return SM.getMacroArgExpandedLocation(FileLoc.getLocWithOffset(Offset)); } /// \brief If \arg Loc is a loaded location from the preamble, returns /// the corresponding local location of the main file, otherwise it returns /// \arg Loc. SourceLocation ASTUnit::mapLocationFromPreamble(SourceLocation Loc) { FileID PreambleID; if (SourceMgr) PreambleID = SourceMgr->getPreambleFileID(); if (Loc.isInvalid() || Preamble.empty() || PreambleID.isInvalid()) return Loc; unsigned Offs; if (SourceMgr->isInFileID(Loc, PreambleID, &Offs) && Offs < Preamble.size()) { SourceLocation FileLoc = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()); return FileLoc.getLocWithOffset(Offs); } return Loc; } /// \brief If \arg Loc is a local location of the main file but inside the /// preamble chunk, returns the corresponding loaded location from the /// preamble, otherwise it returns \arg Loc. SourceLocation ASTUnit::mapLocationToPreamble(SourceLocation Loc) { FileID PreambleID; if (SourceMgr) PreambleID = SourceMgr->getPreambleFileID(); if (Loc.isInvalid() || Preamble.empty() || PreambleID.isInvalid()) return Loc; unsigned Offs; if (SourceMgr->isInFileID(Loc, SourceMgr->getMainFileID(), &Offs) && Offs < Preamble.size()) { SourceLocation FileLoc = SourceMgr->getLocForStartOfFile(PreambleID); return FileLoc.getLocWithOffset(Offs); } return Loc; } bool ASTUnit::isInPreambleFileID(SourceLocation Loc) { FileID FID; if (SourceMgr) FID = SourceMgr->getPreambleFileID(); if (Loc.isInvalid() || FID.isInvalid()) return false; return SourceMgr->isInFileID(Loc, FID); } bool ASTUnit::isInMainFileID(SourceLocation Loc) { FileID FID; if (SourceMgr) FID = SourceMgr->getMainFileID(); if (Loc.isInvalid() || FID.isInvalid()) return false; return SourceMgr->isInFileID(Loc, FID); } SourceLocation ASTUnit::getEndOfPreambleFileID() { FileID FID; if (SourceMgr) FID = SourceMgr->getPreambleFileID(); if (FID.isInvalid()) return SourceLocation(); return SourceMgr->getLocForEndOfFile(FID); } SourceLocation ASTUnit::getStartOfMainFileID() { FileID FID; if (SourceMgr) FID = SourceMgr->getMainFileID(); if (FID.isInvalid()) return SourceLocation(); return SourceMgr->getLocForStartOfFile(FID); } llvm::iterator_range ASTUnit::getLocalPreprocessingEntities() const { if (isMainFileAST()) { serialization::ModuleFile & Mod = Reader->getModuleManager().getPrimaryModule(); return Reader->getModulePreprocessedEntities(Mod); } if (PreprocessingRecord *PPRec = PP->getPreprocessingRecord()) return llvm::make_range(PPRec->local_begin(), PPRec->local_end()); return llvm::make_range(PreprocessingRecord::iterator(), PreprocessingRecord::iterator()); } bool ASTUnit::visitLocalTopLevelDecls(void *context, DeclVisitorFn Fn) { if (isMainFileAST()) { serialization::ModuleFile & Mod = Reader->getModuleManager().getPrimaryModule(); for (const Decl *D : Reader->getModuleFileLevelDecls(Mod)) { if (!Fn(context, D)) return false; } return true; } for (ASTUnit::top_level_iterator TL = top_level_begin(), TLEnd = top_level_end(); TL != TLEnd; ++TL) { if (!Fn(context, *TL)) return false; } return true; } const FileEntry *ASTUnit::getPCHFile() { if (!Reader) return nullptr; serialization::ModuleFile *Mod = nullptr; Reader->getModuleManager().visit([&Mod](serialization::ModuleFile &M) { switch (M.Kind) { case serialization::MK_ImplicitModule: case serialization::MK_ExplicitModule: case serialization::MK_PrebuiltModule: return true; // skip dependencies. case serialization::MK_PCH: Mod = &M; return true; // found it. case serialization::MK_Preamble: return false; // look in dependencies. case serialization::MK_MainFile: return false; // look in dependencies. } return true; }); if (Mod) return Mod->File; return nullptr; } bool ASTUnit::isModuleFile() { return isMainFileAST() && getLangOpts().isCompilingModule(); } InputKind ASTUnit::getInputKind() const { auto &LangOpts = getLangOpts(); InputKind::Language Lang; if (LangOpts.OpenCL) Lang = InputKind::OpenCL; else if (LangOpts.CUDA) Lang = InputKind::CUDA; else if (LangOpts.RenderScript) Lang = InputKind::RenderScript; else if (LangOpts.CPlusPlus) Lang = LangOpts.ObjC1 ? InputKind::ObjCXX : InputKind::CXX; else Lang = LangOpts.ObjC1 ? InputKind::ObjC : InputKind::C; InputKind::Format Fmt = InputKind::Source; if (LangOpts.getCompilingModule() == LangOptions::CMK_ModuleMap) Fmt = InputKind::ModuleMap; // We don't know if input was preprocessed. Assume not. bool PP = false; return InputKind(Lang, Fmt, PP); } void ASTUnit::PreambleData::countLines() const { NumLines = 0; if (empty()) return; NumLines = std::count(Buffer.begin(), Buffer.end(), '\n'); if (Buffer.back() != '\n') ++NumLines; } #ifndef NDEBUG ASTUnit::ConcurrencyState::ConcurrencyState() { Mutex = new llvm::sys::MutexImpl(/*recursive=*/true); } ASTUnit::ConcurrencyState::~ConcurrencyState() { delete static_cast(Mutex); } void ASTUnit::ConcurrencyState::start() { bool acquired = static_cast(Mutex)->tryacquire(); assert(acquired && "Concurrent access to ASTUnit!"); } void ASTUnit::ConcurrencyState::finish() { static_cast(Mutex)->release(); } #else // NDEBUG ASTUnit::ConcurrencyState::ConcurrencyState() { Mutex = nullptr; } ASTUnit::ConcurrencyState::~ConcurrencyState() {} void ASTUnit::ConcurrencyState::start() {} void ASTUnit::ConcurrencyState::finish() {} #endif // NDEBUG Index: include/clang/Frontend/ASTUnit.h =================================================================== --- include/clang/Frontend/ASTUnit.h (revision 305060) +++ include/clang/Frontend/ASTUnit.h (revision 305061) @@ -1,959 +1,967 @@ //===--- ASTUnit.h - ASTUnit utility ----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // ASTUnit utility class. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_FRONTEND_ASTUNIT_H #define LLVM_CLANG_FRONTEND_ASTUNIT_H #include "clang-c/Index.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetOptions.h" #include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/PreprocessingRecord.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Serialization/ASTBitCodes.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/MD5.h" #include #include #include #include #include #include namespace llvm { class MemoryBuffer; } namespace clang { class Sema; class ASTContext; class ASTReader; class CompilerInvocation; class CompilerInstance; class Decl; class DiagnosticsEngine; class FileEntry; class FileManager; class HeaderSearch; class InputKind; class MemoryBufferCache; class Preprocessor; class PreprocessorOptions; class PCHContainerOperations; class PCHContainerReader; class TargetInfo; class FrontendAction; class ASTDeserializationListener; namespace vfs { class FileSystem; } /// \brief Utility class for loading a ASTContext from an AST file. /// class ASTUnit : public ModuleLoader { public: struct StandaloneFixIt { std::pair RemoveRange; std::pair InsertFromRange; std::string CodeToInsert; bool BeforePreviousInsertions; }; struct StandaloneDiagnostic { unsigned ID; DiagnosticsEngine::Level Level; std::string Message; std::string Filename; unsigned LocOffset; std::vector > Ranges; std::vector FixIts; }; private: std::shared_ptr LangOpts; IntrusiveRefCntPtr Diagnostics; IntrusiveRefCntPtr FileMgr; IntrusiveRefCntPtr SourceMgr; IntrusiveRefCntPtr PCMCache; std::unique_ptr HeaderInfo; IntrusiveRefCntPtr Target; std::shared_ptr PP; IntrusiveRefCntPtr Ctx; std::shared_ptr TargetOpts; std::shared_ptr HSOpts; std::shared_ptr PPOpts; IntrusiveRefCntPtr Reader; bool HadModuleLoaderFatalFailure; struct ASTWriterData; std::unique_ptr WriterData; FileSystemOptions FileSystemOpts; /// \brief The AST consumer that received information about the translation /// unit as it was parsed or loaded. std::unique_ptr Consumer; /// \brief The semantic analysis object used to type-check the translation /// unit. std::unique_ptr TheSema; /// Optional owned invocation, just used to make the invocation used in /// LoadFromCommandLine available. std::shared_ptr Invocation; // OnlyLocalDecls - when true, walking this AST should only visit declarations // that come from the AST itself, not from included precompiled headers. // FIXME: This is temporary; eventually, CIndex will always do this. bool OnlyLocalDecls; /// \brief Whether to capture any diagnostics produced. bool CaptureDiagnostics; /// \brief Track whether the main file was loaded from an AST or not. bool MainFileIsAST; /// \brief What kind of translation unit this AST represents. TranslationUnitKind TUKind; /// \brief Whether we should time each operation. bool WantTiming; /// \brief Whether the ASTUnit should delete the remapped buffers. bool OwnsRemappedFileBuffers; /// Track the top-level decls which appeared in an ASTUnit which was loaded /// from a source file. // // FIXME: This is just an optimization hack to avoid deserializing large parts // of a PCH file when using the Index library on an ASTUnit loaded from // source. In the long term we should make the Index library use efficient and // more scalable search mechanisms. std::vector TopLevelDecls; /// \brief Sorted (by file offset) vector of pairs of file offset/Decl. typedef SmallVector, 64> LocDeclsTy; typedef llvm::DenseMap FileDeclsTy; /// \brief Map from FileID to the file-level declarations that it contains. /// The files and decls are only local (and non-preamble) ones. FileDeclsTy FileDecls; /// The name of the original source file used to generate this ASTUnit. std::string OriginalSourceFile; /// \brief The set of diagnostics produced when creating the preamble. SmallVector PreambleDiagnostics; /// \brief The set of diagnostics produced when creating this /// translation unit. SmallVector StoredDiagnostics; /// \brief The set of diagnostics produced when failing to parse, e.g. due /// to failure to load the PCH. SmallVector FailedParseDiagnostics; /// \brief The number of stored diagnostics that come from the driver /// itself. /// /// Diagnostics that come from the driver are retained from one parse to /// the next. unsigned NumStoredDiagnosticsFromDriver; /// \brief Counter that determines when we want to try building a /// precompiled preamble. /// /// If zero, we will never build a precompiled preamble. Otherwise, /// it's treated as a counter that decrements each time we reparse /// without the benefit of a precompiled preamble. When it hits 1, /// we'll attempt to rebuild the precompiled header. This way, if /// building the precompiled preamble fails, we won't try again for /// some number of calls. unsigned PreambleRebuildCounter; + /// \brief Cache pairs "filename - source location" + /// + /// Cache contains only source locations from preamble so it is + /// guaranteed that they stay valid when the SourceManager is recreated. + /// This cache is used when loading preambule to increase performance + /// of that loading. It must be cleared when preamble is recreated. + llvm::StringMap PreambleSrcLocCache; + public: class PreambleData { const FileEntry *File; std::vector Buffer; mutable unsigned NumLines; public: PreambleData() : File(nullptr), NumLines(0) { } void assign(const FileEntry *F, const char *begin, const char *end) { File = F; Buffer.assign(begin, end); NumLines = 0; } void clear() { Buffer.clear(); File = nullptr; NumLines = 0; } size_t size() const { return Buffer.size(); } bool empty() const { return Buffer.empty(); } const char *getBufferStart() const { return &Buffer[0]; } unsigned getNumLines() const { if (NumLines) return NumLines; countLines(); return NumLines; } SourceRange getSourceRange(const SourceManager &SM) const { SourceLocation FileLoc = SM.getLocForStartOfFile(SM.getPreambleFileID()); return SourceRange(FileLoc, FileLoc.getLocWithOffset(size()-1)); } private: void countLines() const; }; const PreambleData &getPreambleData() const { return Preamble; } /// Data used to determine if a file used in the preamble has been changed. struct PreambleFileHash { /// All files have size set. off_t Size; /// Modification time is set for files that are on disk. For memory /// buffers it is zero. time_t ModTime; /// Memory buffers have MD5 instead of modification time. We don't /// compute MD5 for on-disk files because we hope that modification time is /// enough to tell if the file was changed. llvm::MD5::MD5Result MD5; static PreambleFileHash createForFile(off_t Size, time_t ModTime); static PreambleFileHash createForMemoryBuffer(const llvm::MemoryBuffer *Buffer); friend bool operator==(const PreambleFileHash &LHS, const PreambleFileHash &RHS); friend bool operator!=(const PreambleFileHash &LHS, const PreambleFileHash &RHS) { return !(LHS == RHS); } }; private: /// \brief The contents of the preamble that has been precompiled to /// \c PreambleFile. PreambleData Preamble; /// \brief Whether the preamble ends at the start of a new line. /// /// Used to inform the lexer as to whether it's starting at the beginning of /// a line after skipping the preamble. bool PreambleEndsAtStartOfLine; /// \brief Keeps track of the files that were used when computing the /// preamble, with both their buffer size and their modification time. /// /// If any of the files have changed from one compile to the next, /// the preamble must be thrown away. llvm::StringMap FilesInPreamble; /// \brief When non-NULL, this is the buffer used to store the contents of /// the main file when it has been padded for use with the precompiled /// preamble. std::unique_ptr SavedMainFileBuffer; /// \brief When non-NULL, this is the buffer used to store the /// contents of the preamble when it has been padded to build the /// precompiled preamble. std::unique_ptr PreambleBuffer; /// \brief The number of warnings that occurred while parsing the preamble. /// /// This value will be used to restore the state of the \c DiagnosticsEngine /// object when re-using the precompiled preamble. Note that only the /// number of warnings matters, since we will not save the preamble /// when any errors are present. unsigned NumWarningsInPreamble; /// \brief A list of the serialization ID numbers for each of the top-level /// declarations parsed within the precompiled preamble. std::vector TopLevelDeclsInPreamble; /// \brief Whether we should be caching code-completion results. bool ShouldCacheCodeCompletionResults : 1; /// \brief Whether to include brief documentation within the set of code /// completions cached. bool IncludeBriefCommentsInCodeCompletion : 1; /// \brief True if non-system source files should be treated as volatile /// (likely to change while trying to use them). bool UserFilesAreVolatile : 1; static void ConfigureDiags(IntrusiveRefCntPtr Diags, ASTUnit &AST, bool CaptureDiagnostics); void TranslateStoredDiagnostics(FileManager &FileMgr, SourceManager &SrcMan, const SmallVectorImpl &Diags, SmallVectorImpl &Out); void clearFileLevelDecls(); public: /// \brief A cached code-completion result, which may be introduced in one of /// many different contexts. struct CachedCodeCompletionResult { /// \brief The code-completion string corresponding to this completion /// result. CodeCompletionString *Completion; /// \brief A bitmask that indicates which code-completion contexts should /// contain this completion result. /// /// The bits in the bitmask correspond to the values of /// CodeCompleteContext::Kind. To map from a completion context kind to a /// bit, shift 1 by that number of bits. Many completions can occur in /// several different contexts. uint64_t ShowInContexts; /// \brief The priority given to this code-completion result. unsigned Priority; /// \brief The libclang cursor kind corresponding to this code-completion /// result. CXCursorKind Kind; /// \brief The availability of this code-completion result. CXAvailabilityKind Availability; /// \brief The simplified type class for a non-macro completion result. SimplifiedTypeClass TypeClass; /// \brief The type of a non-macro completion result, stored as a unique /// integer used by the string map of cached completion types. /// /// This value will be zero if the type is not known, or a unique value /// determined by the formatted type string. Se \c CachedCompletionTypes /// for more information. unsigned Type; }; /// \brief Retrieve the mapping from formatted type names to unique type /// identifiers. llvm::StringMap &getCachedCompletionTypes() { return CachedCompletionTypes; } /// \brief Retrieve the allocator used to cache global code completions. std::shared_ptr getCachedCompletionAllocator() { return CachedCompletionAllocator; } CodeCompletionTUInfo &getCodeCompletionTUInfo() { if (!CCTUInfo) CCTUInfo = llvm::make_unique( std::make_shared()); return *CCTUInfo; } private: /// \brief Allocator used to store cached code completions. std::shared_ptr CachedCompletionAllocator; std::unique_ptr CCTUInfo; /// \brief The set of cached code-completion results. std::vector CachedCompletionResults; /// \brief A mapping from the formatted type name to a unique number for that /// type, which is used for type equality comparisons. llvm::StringMap CachedCompletionTypes; /// \brief A string hash of the top-level declaration and macro definition /// names processed the last time that we reparsed the file. /// /// This hash value is used to determine when we need to refresh the /// global code-completion cache. unsigned CompletionCacheTopLevelHashValue; /// \brief A string hash of the top-level declaration and macro definition /// names processed the last time that we reparsed the precompiled preamble. /// /// This hash value is used to determine when we need to refresh the /// global code-completion cache after a rebuild of the precompiled preamble. unsigned PreambleTopLevelHashValue; /// \brief The current hash value for the top-level declaration and macro /// definition names unsigned CurrentTopLevelHashValue; /// \brief Bit used by CIndex to mark when a translation unit may be in an /// inconsistent state, and is not safe to free. unsigned UnsafeToFree : 1; /// \brief Cache any "global" code-completion results, so that we can avoid /// recomputing them with each completion. void CacheCodeCompletionResults(); /// \brief Clear out and deallocate void ClearCachedCompletionResults(); ASTUnit(const ASTUnit &) = delete; void operator=(const ASTUnit &) = delete; explicit ASTUnit(bool MainFileIsAST); bool Parse(std::shared_ptr PCHContainerOps, std::unique_ptr OverrideMainBuffer, IntrusiveRefCntPtr VFS); struct ComputedPreamble { llvm::MemoryBuffer *Buffer; std::unique_ptr Owner; unsigned Size; bool PreambleEndsAtStartOfLine; ComputedPreamble(llvm::MemoryBuffer *Buffer, std::unique_ptr Owner, unsigned Size, bool PreambleEndsAtStartOfLine) : Buffer(Buffer), Owner(std::move(Owner)), Size(Size), PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {} }; ComputedPreamble ComputePreamble(CompilerInvocation &Invocation, unsigned MaxLines, IntrusiveRefCntPtr VFS); std::unique_ptr getMainBufferWithPrecompiledPreamble( std::shared_ptr PCHContainerOps, const CompilerInvocation &PreambleInvocationIn, IntrusiveRefCntPtr VFS, bool AllowRebuild = true, unsigned MaxLines = 0); void RealizeTopLevelDeclsFromPreamble(); /// \brief Transfers ownership of the objects (like SourceManager) from /// \param CI to this ASTUnit. void transferASTDataFromCompilerInstance(CompilerInstance &CI); /// \brief Allows us to assert that ASTUnit is not being used concurrently, /// which is not supported. /// /// Clients should create instances of the ConcurrencyCheck class whenever /// using the ASTUnit in a way that isn't intended to be concurrent, which is /// just about any usage. /// Becomes a noop in release mode; only useful for debug mode checking. class ConcurrencyState { void *Mutex; // a llvm::sys::MutexImpl in debug; public: ConcurrencyState(); ~ConcurrencyState(); void start(); void finish(); }; ConcurrencyState ConcurrencyCheckValue; public: class ConcurrencyCheck { ASTUnit &Self; public: explicit ConcurrencyCheck(ASTUnit &Self) : Self(Self) { Self.ConcurrencyCheckValue.start(); } ~ConcurrencyCheck() { Self.ConcurrencyCheckValue.finish(); } }; friend class ConcurrencyCheck; ~ASTUnit() override; bool isMainFileAST() const { return MainFileIsAST; } bool isUnsafeToFree() const { return UnsafeToFree; } void setUnsafeToFree(bool Value) { UnsafeToFree = Value; } const DiagnosticsEngine &getDiagnostics() const { return *Diagnostics; } DiagnosticsEngine &getDiagnostics() { return *Diagnostics; } const SourceManager &getSourceManager() const { return *SourceMgr; } SourceManager &getSourceManager() { return *SourceMgr; } const Preprocessor &getPreprocessor() const { return *PP; } Preprocessor &getPreprocessor() { return *PP; } std::shared_ptr getPreprocessorPtr() const { return PP; } const ASTContext &getASTContext() const { return *Ctx; } ASTContext &getASTContext() { return *Ctx; } void setASTContext(ASTContext *ctx) { Ctx = ctx; } void setPreprocessor(std::shared_ptr pp); bool hasSema() const { return (bool)TheSema; } Sema &getSema() const { assert(TheSema && "ASTUnit does not have a Sema object!"); return *TheSema; } const LangOptions &getLangOpts() const { assert(LangOpts && "ASTUnit does not have language options"); return *LangOpts; } const HeaderSearchOptions &getHeaderSearchOpts() const { assert(HSOpts && "ASTUnit does not have header search options"); return *HSOpts; } const PreprocessorOptions &getPreprocessorOpts() const { assert(PPOpts && "ASTUnit does not have preprocessor options"); return *PPOpts; } const FileManager &getFileManager() const { return *FileMgr; } FileManager &getFileManager() { return *FileMgr; } const FileSystemOptions &getFileSystemOpts() const { return FileSystemOpts; } IntrusiveRefCntPtr getASTReader() const; StringRef getOriginalSourceFileName() { return OriginalSourceFile; } ASTMutationListener *getASTMutationListener(); ASTDeserializationListener *getDeserializationListener(); bool getOnlyLocalDecls() const { return OnlyLocalDecls; } bool getOwnsRemappedFileBuffers() const { return OwnsRemappedFileBuffers; } void setOwnsRemappedFileBuffers(bool val) { OwnsRemappedFileBuffers = val; } StringRef getMainFileName() const; /// \brief If this ASTUnit came from an AST file, returns the filename for it. StringRef getASTFileName() const; typedef std::vector::iterator top_level_iterator; top_level_iterator top_level_begin() { assert(!isMainFileAST() && "Invalid call for AST based ASTUnit!"); if (!TopLevelDeclsInPreamble.empty()) RealizeTopLevelDeclsFromPreamble(); return TopLevelDecls.begin(); } top_level_iterator top_level_end() { assert(!isMainFileAST() && "Invalid call for AST based ASTUnit!"); if (!TopLevelDeclsInPreamble.empty()) RealizeTopLevelDeclsFromPreamble(); return TopLevelDecls.end(); } std::size_t top_level_size() const { assert(!isMainFileAST() && "Invalid call for AST based ASTUnit!"); return TopLevelDeclsInPreamble.size() + TopLevelDecls.size(); } bool top_level_empty() const { assert(!isMainFileAST() && "Invalid call for AST based ASTUnit!"); return TopLevelDeclsInPreamble.empty() && TopLevelDecls.empty(); } /// \brief Add a new top-level declaration. void addTopLevelDecl(Decl *D) { TopLevelDecls.push_back(D); } /// \brief Add a new local file-level declaration. void addFileLevelDecl(Decl *D); /// \brief Get the decls that are contained in a file in the Offset/Length /// range. \p Length can be 0 to indicate a point at \p Offset instead of /// a range. void findFileRegionDecls(FileID File, unsigned Offset, unsigned Length, SmallVectorImpl &Decls); /// \brief Add a new top-level declaration, identified by its ID in /// the precompiled preamble. void addTopLevelDeclFromPreamble(serialization::DeclID D) { TopLevelDeclsInPreamble.push_back(D); } /// \brief Retrieve a reference to the current top-level name hash value. /// /// Note: This is used internally by the top-level tracking action unsigned &getCurrentTopLevelHashValue() { return CurrentTopLevelHashValue; } /// \brief Get the source location for the given file:line:col triplet. /// /// The difference with SourceManager::getLocation is that this method checks /// whether the requested location points inside the precompiled preamble /// in which case the returned source location will be a "loaded" one. SourceLocation getLocation(const FileEntry *File, unsigned Line, unsigned Col) const; /// \brief Get the source location for the given file:offset pair. SourceLocation getLocation(const FileEntry *File, unsigned Offset) const; /// \brief If \p Loc is a loaded location from the preamble, returns /// the corresponding local location of the main file, otherwise it returns /// \p Loc. SourceLocation mapLocationFromPreamble(SourceLocation Loc); /// \brief If \p Loc is a local location of the main file but inside the /// preamble chunk, returns the corresponding loaded location from the /// preamble, otherwise it returns \p Loc. SourceLocation mapLocationToPreamble(SourceLocation Loc); bool isInPreambleFileID(SourceLocation Loc); bool isInMainFileID(SourceLocation Loc); SourceLocation getStartOfMainFileID(); SourceLocation getEndOfPreambleFileID(); /// \see mapLocationFromPreamble. SourceRange mapRangeFromPreamble(SourceRange R) { return SourceRange(mapLocationFromPreamble(R.getBegin()), mapLocationFromPreamble(R.getEnd())); } /// \see mapLocationToPreamble. SourceRange mapRangeToPreamble(SourceRange R) { return SourceRange(mapLocationToPreamble(R.getBegin()), mapLocationToPreamble(R.getEnd())); } // Retrieve the diagnostics associated with this AST typedef StoredDiagnostic *stored_diag_iterator; typedef const StoredDiagnostic *stored_diag_const_iterator; stored_diag_const_iterator stored_diag_begin() const { return StoredDiagnostics.begin(); } stored_diag_iterator stored_diag_begin() { return StoredDiagnostics.begin(); } stored_diag_const_iterator stored_diag_end() const { return StoredDiagnostics.end(); } stored_diag_iterator stored_diag_end() { return StoredDiagnostics.end(); } unsigned stored_diag_size() const { return StoredDiagnostics.size(); } stored_diag_iterator stored_diag_afterDriver_begin() { if (NumStoredDiagnosticsFromDriver > StoredDiagnostics.size()) NumStoredDiagnosticsFromDriver = 0; return StoredDiagnostics.begin() + NumStoredDiagnosticsFromDriver; } typedef std::vector::iterator cached_completion_iterator; cached_completion_iterator cached_completion_begin() { return CachedCompletionResults.begin(); } cached_completion_iterator cached_completion_end() { return CachedCompletionResults.end(); } unsigned cached_completion_size() const { return CachedCompletionResults.size(); } /// \brief Returns an iterator range for the local preprocessing entities /// of the local Preprocessor, if this is a parsed source file, or the loaded /// preprocessing entities of the primary module if this is an AST file. llvm::iterator_range getLocalPreprocessingEntities() const; /// \brief Type for a function iterating over a number of declarations. /// \returns true to continue iteration and false to abort. typedef bool (*DeclVisitorFn)(void *context, const Decl *D); /// \brief Iterate over local declarations (locally parsed if this is a parsed /// source file or the loaded declarations of the primary module if this is an /// AST file). /// \returns true if the iteration was complete or false if it was aborted. bool visitLocalTopLevelDecls(void *context, DeclVisitorFn Fn); /// \brief Get the PCH file if one was included. const FileEntry *getPCHFile(); /// \brief Returns true if the ASTUnit was constructed from a serialized /// module file. bool isModuleFile(); std::unique_ptr getBufferForFile(StringRef Filename, std::string *ErrorStr = nullptr); /// \brief Determine what kind of translation unit this AST represents. TranslationUnitKind getTranslationUnitKind() const { return TUKind; } /// \brief Determine the input kind this AST unit represents. InputKind getInputKind() const; /// \brief A mapping from a file name to the memory buffer that stores the /// remapped contents of that file. typedef std::pair RemappedFile; /// \brief Create a ASTUnit. Gets ownership of the passed CompilerInvocation. static std::unique_ptr create(std::shared_ptr CI, IntrusiveRefCntPtr Diags, bool CaptureDiagnostics, bool UserFilesAreVolatile); /// \brief Create a ASTUnit from an AST file. /// /// \param Filename - The AST file to load. /// /// \param PCHContainerRdr - The PCHContainerOperations to use for loading and /// creating modules. /// \param Diags - The diagnostics engine to use for reporting errors; its /// lifetime is expected to extend past that of the returned ASTUnit. /// /// \returns - The initialized ASTUnit or null if the AST failed to load. static std::unique_ptr LoadFromASTFile( const std::string &Filename, const PCHContainerReader &PCHContainerRdr, IntrusiveRefCntPtr Diags, const FileSystemOptions &FileSystemOpts, bool UseDebugInfo = false, bool OnlyLocalDecls = false, ArrayRef RemappedFiles = None, bool CaptureDiagnostics = false, bool AllowPCHWithCompilerErrors = false, bool UserFilesAreVolatile = false); private: /// \brief Helper function for \c LoadFromCompilerInvocation() and /// \c LoadFromCommandLine(), which loads an AST from a compiler invocation. /// /// \param PrecompilePreambleAfterNParses After how many parses the preamble /// of this translation unit should be precompiled, to improve the performance /// of reparsing. Set to zero to disable preambles. /// /// \param VFS - A vfs::FileSystem to be used for all file accesses. Note that /// preamble is saved to a temporary directory on a RealFileSystem, so in order /// for it to be loaded correctly, VFS should have access to it(i.e., be an /// overlay over RealFileSystem). /// /// \returns \c true if a catastrophic failure occurred (which means that the /// \c ASTUnit itself is invalid), or \c false otherwise. bool LoadFromCompilerInvocation( std::shared_ptr PCHContainerOps, unsigned PrecompilePreambleAfterNParses, IntrusiveRefCntPtr VFS); public: /// \brief Create an ASTUnit from a source file, via a CompilerInvocation /// object, by invoking the optionally provided ASTFrontendAction. /// /// \param CI - The compiler invocation to use; it must have exactly one input /// source file. The ASTUnit takes ownership of the CompilerInvocation object. /// /// \param PCHContainerOps - The PCHContainerOperations to use for loading and /// creating modules. /// /// \param Diags - The diagnostics engine to use for reporting errors; its /// lifetime is expected to extend past that of the returned ASTUnit. /// /// \param Action - The ASTFrontendAction to invoke. Its ownership is not /// transferred. /// /// \param Unit - optionally an already created ASTUnit. Its ownership is not /// transferred. /// /// \param Persistent - if true the returned ASTUnit will be complete. /// false means the caller is only interested in getting info through the /// provided \see Action. /// /// \param ErrAST - If non-null and parsing failed without any AST to return /// (e.g. because the PCH could not be loaded), this accepts the ASTUnit /// mainly to allow the caller to see the diagnostics. /// This will only receive an ASTUnit if a new one was created. If an already /// created ASTUnit was passed in \p Unit then the caller can check that. /// static ASTUnit *LoadFromCompilerInvocationAction( std::shared_ptr CI, std::shared_ptr PCHContainerOps, IntrusiveRefCntPtr Diags, FrontendAction *Action = nullptr, ASTUnit *Unit = nullptr, bool Persistent = true, StringRef ResourceFilesPath = StringRef(), bool OnlyLocalDecls = false, bool CaptureDiagnostics = false, unsigned PrecompilePreambleAfterNParses = 0, bool CacheCodeCompletionResults = false, bool IncludeBriefCommentsInCodeCompletion = false, bool UserFilesAreVolatile = false, std::unique_ptr *ErrAST = nullptr); /// LoadFromCompilerInvocation - Create an ASTUnit from a source file, via a /// CompilerInvocation object. /// /// \param CI - The compiler invocation to use; it must have exactly one input /// source file. The ASTUnit takes ownership of the CompilerInvocation object. /// /// \param PCHContainerOps - The PCHContainerOperations to use for loading and /// creating modules. /// /// \param Diags - The diagnostics engine to use for reporting errors; its /// lifetime is expected to extend past that of the returned ASTUnit. // // FIXME: Move OnlyLocalDecls, UseBumpAllocator to setters on the ASTUnit, we // shouldn't need to specify them at construction time. static std::unique_ptr LoadFromCompilerInvocation( std::shared_ptr CI, std::shared_ptr PCHContainerOps, IntrusiveRefCntPtr Diags, FileManager *FileMgr, bool OnlyLocalDecls = false, bool CaptureDiagnostics = false, unsigned PrecompilePreambleAfterNParses = 0, TranslationUnitKind TUKind = TU_Complete, bool CacheCodeCompletionResults = false, bool IncludeBriefCommentsInCodeCompletion = false, bool UserFilesAreVolatile = false); /// LoadFromCommandLine - Create an ASTUnit from a vector of command line /// arguments, which must specify exactly one source file. /// /// \param ArgBegin - The beginning of the argument vector. /// /// \param ArgEnd - The end of the argument vector. /// /// \param PCHContainerOps - The PCHContainerOperations to use for loading and /// creating modules. /// /// \param Diags - The diagnostics engine to use for reporting errors; its /// lifetime is expected to extend past that of the returned ASTUnit. /// /// \param ResourceFilesPath - The path to the compiler resource files. /// /// \param ModuleFormat - If provided, uses the specific module format. /// /// \param ErrAST - If non-null and parsing failed without any AST to return /// (e.g. because the PCH could not be loaded), this accepts the ASTUnit /// mainly to allow the caller to see the diagnostics. /// /// \param VFS - A vfs::FileSystem to be used for all file accesses. Note that /// preamble is saved to a temporary directory on a RealFileSystem, so in order /// for it to be loaded correctly, VFS should have access to it(i.e., be an /// overlay over RealFileSystem). RealFileSystem will be used if \p VFS is nullptr. /// // FIXME: Move OnlyLocalDecls, UseBumpAllocator to setters on the ASTUnit, we // shouldn't need to specify them at construction time. static ASTUnit *LoadFromCommandLine( const char **ArgBegin, const char **ArgEnd, std::shared_ptr PCHContainerOps, IntrusiveRefCntPtr Diags, StringRef ResourceFilesPath, bool OnlyLocalDecls = false, bool CaptureDiagnostics = false, ArrayRef RemappedFiles = None, bool RemappedFilesKeepOriginalName = true, unsigned PrecompilePreambleAfterNParses = 0, TranslationUnitKind TUKind = TU_Complete, bool CacheCodeCompletionResults = false, bool IncludeBriefCommentsInCodeCompletion = false, bool AllowPCHWithCompilerErrors = false, bool SkipFunctionBodies = false, bool SingleFileParse = false, bool UserFilesAreVolatile = false, bool ForSerialization = false, llvm::Optional ModuleFormat = llvm::None, std::unique_ptr *ErrAST = nullptr, IntrusiveRefCntPtr VFS = nullptr); /// \brief Reparse the source files using the same command-line options that /// were originally used to produce this translation unit. /// /// \param VFS - A vfs::FileSystem to be used for all file accesses. Note that /// preamble is saved to a temporary directory on a RealFileSystem, so in order /// for it to be loaded correctly, VFS should give an access to this(i.e. be an /// overlay over RealFileSystem). FileMgr->getVirtualFileSystem() will be used if /// \p VFS is nullptr. /// /// \returns True if a failure occurred that causes the ASTUnit not to /// contain any translation-unit information, false otherwise. bool Reparse(std::shared_ptr PCHContainerOps, ArrayRef RemappedFiles = None, IntrusiveRefCntPtr VFS = nullptr); /// \brief Free data that will be re-generated on the next parse. /// /// Preamble-related data is not affected. void ResetForParse(); /// \brief Perform code completion at the given file, line, and /// column within this translation unit. /// /// \param File The file in which code completion will occur. /// /// \param Line The line at which code completion will occur. /// /// \param Column The column at which code completion will occur. /// /// \param IncludeMacros Whether to include macros in the code-completion /// results. /// /// \param IncludeCodePatterns Whether to include code patterns (such as a /// for loop) in the code-completion results. /// /// \param IncludeBriefComments Whether to include brief documentation within /// the set of code completions returned. /// /// FIXME: The Diag, LangOpts, SourceMgr, FileMgr, StoredDiagnostics, and /// OwnedBuffers parameters are all disgusting hacks. They will go away. void CodeComplete(StringRef File, unsigned Line, unsigned Column, ArrayRef RemappedFiles, bool IncludeMacros, bool IncludeCodePatterns, bool IncludeBriefComments, CodeCompleteConsumer &Consumer, std::shared_ptr PCHContainerOps, DiagnosticsEngine &Diag, LangOptions &LangOpts, SourceManager &SourceMgr, FileManager &FileMgr, SmallVectorImpl &StoredDiagnostics, SmallVectorImpl &OwnedBuffers); /// \brief Save this translation unit to a file with the given name. /// /// \returns true if there was a file error or false if the save was /// successful. bool Save(StringRef File); /// \brief Serialize this translation unit with the given output stream. /// /// \returns True if an error occurred, false otherwise. bool serialize(raw_ostream &OS); ModuleLoadResult loadModule(SourceLocation ImportLoc, ModuleIdPath Path, Module::NameVisibilityKind Visibility, bool IsInclusionDirective) override { // ASTUnit doesn't know how to load modules (not that this matters). return ModuleLoadResult(); } void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility, SourceLocation ImportLoc) override {} GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override { return nullptr; } bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override { return 0; } }; } // namespace clang #endif