diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Frontend')
28 files changed, 4677 insertions, 2337 deletions
diff --git a/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp b/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp index 54bb282..390ae09 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp @@ -66,9 +66,10 @@ namespace { this->Context = &Context; } - virtual void HandleTopLevelDecl(DeclGroupRef D) { + virtual bool HandleTopLevelDecl(DeclGroupRef D) { for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) HandleTopLevelSingleDecl(*I); + return true; } void HandleTopLevelSingleDecl(Decl *D); diff --git a/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp b/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp index cb195d1..9feb3de 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp @@ -26,8 +26,7 @@ bool ASTMergeAction::BeginSourceFileAction(CompilerInstance &CI, // FIXME: This is a hack. We need a better way to communicate the // AST file, compiler instance, and file name than member variables // of FrontendAction. - AdaptedAction->setCurrentFile(getCurrentFile(), getCurrentFileKind(), - takeCurrentASTUnit()); + AdaptedAction->setCurrentInput(getCurrentInput(), takeCurrentASTUnit()); AdaptedAction->setCompilerInstance(&CI); return AdaptedAction->BeginSourceFileAction(CI, Filename); } @@ -35,13 +34,13 @@ bool ASTMergeAction::BeginSourceFileAction(CompilerInstance &CI, void ASTMergeAction::ExecuteAction() { CompilerInstance &CI = getCompilerInstance(); CI.getDiagnostics().getClient()->BeginSourceFile( - CI.getASTContext().getLangOptions()); + CI.getASTContext().getLangOpts()); CI.getDiagnostics().SetArgToStringFn(&FormatASTNodeDiagnosticArgument, &CI.getASTContext()); - llvm::IntrusiveRefCntPtr<DiagnosticIDs> + IntrusiveRefCntPtr<DiagnosticIDs> DiagIDs(CI.getDiagnostics().getDiagnosticIDs()); for (unsigned I = 0, N = ASTFiles.size(); I != N; ++I) { - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> + IntrusiveRefCntPtr<DiagnosticsEngine> Diags(new DiagnosticsEngine(DiagIDs, CI.getDiagnostics().getClient(), /*ShouldOwnClient=*/false)); ASTUnit *Unit = ASTUnit::LoadFromASTFile(ASTFiles[I], Diags, @@ -80,8 +79,8 @@ void ASTMergeAction::EndSourceFileAction() { } ASTMergeAction::ASTMergeAction(FrontendAction *AdaptedAction, - std::string *ASTFiles, unsigned NumASTFiles) - : AdaptedAction(AdaptedAction), ASTFiles(ASTFiles, ASTFiles + NumASTFiles) { + ArrayRef<std::string> ASTFiles) + : AdaptedAction(AdaptedAction), ASTFiles(ASTFiles.begin(), ASTFiles.end()) { assert(AdaptedAction && "ASTMergeAction needs an action to adapt"); } diff --git a/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp b/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp index 032adf3..e32fa63 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp @@ -27,6 +27,7 @@ #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/Serialization/ASTReader.h" #include "clang/Serialization/ASTWriter.h" @@ -46,6 +47,7 @@ #include "llvm/Support/Timer.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Mutex.h" +#include "llvm/Support/MutexGuard.h" #include "llvm/Support/CrashRecoveryContext.h" #include <cstdlib> #include <cstdio> @@ -81,6 +83,121 @@ namespace { } } }; + + struct OnDiskData { + /// \brief The file in which the precompiled preamble is stored. + std::string PreambleFile; + + /// \brief Temporary files that should be removed when the ASTUnit is + /// destroyed. + SmallVector<llvm::sys::Path, 4> TemporaryFiles; + + /// \brief Erase temporary files. + void CleanTemporaryFiles(); + + /// \brief Erase the preamble file. + void CleanPreambleFile(); + + /// \brief Erase temporary files and the preamble file. + void Cleanup(); + }; +} + +static llvm::sys::SmartMutex<false> &getOnDiskMutex() { + static llvm::sys::SmartMutex<false> M(/* recursive = */ true); + return M; +} + +static void cleanupOnDiskMapAtExit(void); + +typedef llvm::DenseMap<const ASTUnit *, OnDiskData *> OnDiskDataMap; +static OnDiskDataMap &getOnDiskDataMap() { + static OnDiskDataMap M; + static bool hasRegisteredAtExit = false; + if (!hasRegisteredAtExit) { + hasRegisteredAtExit = true; + atexit(cleanupOnDiskMapAtExit); + } + return M; +} + +static void cleanupOnDiskMapAtExit(void) { + // No mutex required here since we are leaving the program. + OnDiskDataMap &M = getOnDiskDataMap(); + for (OnDiskDataMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { + // 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(); + OnDiskData *&D = M[AU]; + if (!D) + D = new OnDiskData(); + 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(); + delete I->second; + M.erase(AU); + } +} + +static void setPreambleFile(const ASTUnit *AU, llvm::StringRef preambleFile) { + getOnDiskData(AU).PreambleFile = preambleFile; +} + +static const std::string &getPreambleFile(const ASTUnit *AU) { + return getOnDiskData(AU).PreambleFile; +} + +void OnDiskData::CleanTemporaryFiles() { + for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) + TemporaryFiles[I].eraseFromDisk(); + TemporaryFiles.clear(); +} + +void OnDiskData::CleanPreambleFile() { + if (!PreambleFile.empty()) { + llvm::sys::Path(PreambleFile).eraseFromDisk(); + PreambleFile.clear(); + } +} + +void OnDiskData::Cleanup() { + CleanTemporaryFiles(); + CleanPreambleFile(); +} + +void ASTUnit::clearFileLevelDecls() { + for (FileDeclsTy::iterator + I = FileDecls.begin(), E = FileDecls.end(); I != E; ++I) + delete I->second; + FileDecls.clear(); +} + +void ASTUnit::CleanTemporaryFiles() { + getOnDiskData(this).CleanTemporaryFiles(); +} + +void ASTUnit::addTemporaryFile(const llvm::sys::Path &TempFile) { + getOnDiskData(this).TemporaryFiles.push_back(TempFile); } /// \brief After failing to build a precompiled preamble (due to @@ -95,14 +212,14 @@ const unsigned DefaultPreambleRebuildInterval = 5; static llvm::sys::cas_flag ActiveASTUnitObjects; ASTUnit::ASTUnit(bool _MainFileIsAST) - : OnlyLocalDecls(false), CaptureDiagnostics(false), + : Reader(0), OnlyLocalDecls(false), CaptureDiagnostics(false), MainFileIsAST(_MainFileIsAST), TUKind(TU_Complete), WantTiming(getenv("LIBCLANG_TIMING")), OwnsRemappedFileBuffers(true), NumStoredDiagnosticsFromDriver(0), PreambleRebuildCounter(0), SavedMainFileBuffer(0), PreambleBuffer(0), + NumWarningsInPreamble(0), ShouldCacheCodeCompletionResults(false), - NestedMacroExpansions(true), CompletionCacheTopLevelHashValue(0), PreambleTopLevelHashValue(0), CurrentTopLevelHashValue(0), @@ -114,10 +231,11 @@ ASTUnit::ASTUnit(bool _MainFileIsAST) } ASTUnit::~ASTUnit() { - CleanTemporaryFiles(); - if (!PreambleFile.empty()) - llvm::sys::Path(PreambleFile).eraseFromDisk(); - + 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 @@ -143,11 +261,7 @@ ASTUnit::~ASTUnit() { } } -void ASTUnit::CleanTemporaryFiles() { - for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) - TemporaryFiles[I].eraseFromDisk(); - TemporaryFiles.clear(); -} +void ASTUnit::setPreprocessor(Preprocessor *pp) { PP = pp; } /// \brief Determine the set of code-completion contexts in which this /// declaration should be shown. @@ -237,7 +351,8 @@ void ASTUnit::CacheCodeCompletionResults() { typedef CodeCompletionResult Result; SmallVector<Result, 8> Results; CachedCompletionAllocator = new GlobalCodeCompletionAllocator; - TheSema->GatherGlobalCodeCompletions(*CachedCompletionAllocator, Results); + TheSema->GatherGlobalCodeCompletions(*CachedCompletionAllocator, + getCodeCompletionTUInfo(), Results); // Translate global code completions into cached completions. llvm::DenseMap<CanQualType, unsigned> CompletionTypes; @@ -248,9 +363,10 @@ void ASTUnit::CacheCodeCompletionResults() { bool IsNestedNameSpecifier = false; CachedCodeCompletionResult CachedResult; CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema, - *CachedCompletionAllocator); + *CachedCompletionAllocator, + getCodeCompletionTUInfo()); CachedResult.ShowInContexts = getDeclShowContexts(Results[I].Declaration, - Ctx->getLangOptions(), + Ctx->getLangOpts(), IsNestedNameSpecifier); CachedResult.Priority = Results[I].Priority; CachedResult.Kind = Results[I].CursorKind; @@ -283,7 +399,7 @@ void ASTUnit::CacheCodeCompletionResults() { CachedCompletionResults.push_back(CachedResult); /// Handle nested-name-specifiers in C++. - if (TheSema->Context.getLangOptions().CPlusPlus && + if (TheSema->Context.getLangOpts().CPlusPlus && IsNestedNameSpecifier && !Results[I].StartsNestedNameSpecifier) { // The contexts in which a nested-name-specifier can appear in C++. unsigned NNSContexts @@ -312,7 +428,8 @@ void ASTUnit::CacheCodeCompletionResults() { Results[I].StartsNestedNameSpecifier = true; CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema, - *CachedCompletionAllocator); + *CachedCompletionAllocator, + getCodeCompletionTUInfo()); CachedResult.ShowInContexts = RemainingContexts; CachedResult.Priority = CCP_NestedNameSpecifier; CachedResult.TypeClass = STC_Void; @@ -333,7 +450,8 @@ void ASTUnit::CacheCodeCompletionResults() { CachedCodeCompletionResult CachedResult; CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema, - *CachedCompletionAllocator); + *CachedCompletionAllocator, + getCodeCompletionTUInfo()); CachedResult.ShowInContexts = (1 << (CodeCompletionContext::CCC_TopLevel - 1)) | (1 << (CodeCompletionContext::CCC_ObjCInterface - 1)) @@ -378,7 +496,7 @@ class ASTInfoCollector : public ASTReaderListener { ASTContext &Context; LangOptions &LangOpt; HeaderSearch &HSI; - llvm::IntrusiveRefCntPtr<TargetInfo> &Target; + IntrusiveRefCntPtr<TargetInfo> &Target; std::string &Predefines; unsigned &Counter; @@ -388,7 +506,7 @@ class ASTInfoCollector : public ASTReaderListener { public: ASTInfoCollector(Preprocessor &PP, ASTContext &Context, LangOptions &LangOpt, HeaderSearch &HSI, - llvm::IntrusiveRefCntPtr<TargetInfo> &Target, + IntrusiveRefCntPtr<TargetInfo> &Target, std::string &Predefines, unsigned &Counter) : PP(PP), Context(Context), LangOpt(LangOpt), HSI(HSI), Target(Target), @@ -461,6 +579,9 @@ public: DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { // Just drop any diagnostics that come from cloned consumers; they'll // have different source managers anyway. + // FIXME: We'd like to be able to capture these somehow, even if it's just + // file/line/column, because they could occur when parsing module maps or + // building modules on-demand. return new IgnoringDiagConsumer(); } }; @@ -512,7 +633,7 @@ llvm::MemoryBuffer *ASTUnit::getBufferForFile(StringRef Filename, } /// \brief Configure the diagnostics object for use with ASTUnit. -void ASTUnit::ConfigureDiags(llvm::IntrusiveRefCntPtr<DiagnosticsEngine> &Diags, +void ASTUnit::ConfigureDiags(IntrusiveRefCntPtr<DiagnosticsEngine> &Diags, const char **ArgBegin, const char **ArgEnd, ASTUnit &AST, bool CaptureDiagnostics) { if (!Diags.getPtr()) { @@ -530,13 +651,14 @@ void ASTUnit::ConfigureDiags(llvm::IntrusiveRefCntPtr<DiagnosticsEngine> &Diags, } ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, const FileSystemOptions &FileSystemOpts, bool OnlyLocalDecls, RemappedFile *RemappedFiles, unsigned NumRemappedFiles, - bool CaptureDiagnostics) { - llvm::OwningPtr<ASTUnit> AST(new ASTUnit(true)); + bool CaptureDiagnostics, + bool AllowPCHWithCompilerErrors) { + OwningPtr<ASTUnit> AST(new ASTUnit(true)); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> @@ -553,7 +675,10 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, AST->FileMgr = new FileManager(FileSystemOpts); AST->SourceMgr = new SourceManager(AST->getDiagnostics(), AST->getFileManager()); - AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager())); + AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager(), + AST->getDiagnostics(), + AST->ASTFileLangOpts, + /*Target=*/0)); for (unsigned I = 0; I != NumRemappedFiles; ++I) { FilenameOrMemBuf fileOrBuf = RemappedFiles[I].second; @@ -608,7 +733,7 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, std::string Predefines; unsigned Counter; - llvm::OwningPtr<ASTReader> Reader; + OwningPtr<ASTReader> Reader; AST->PP = new Preprocessor(AST->getDiagnostics(), AST->ASTFileLangOpts, /*Target=*/0, AST->getSourceManager(), HeaderInfo, @@ -628,7 +753,11 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, /*DelayInitialization=*/true); ASTContext &Context = *AST->Ctx; - Reader.reset(new ASTReader(PP, Context)); + Reader.reset(new ASTReader(PP, Context, + /*isysroot=*/"", + /*DisableValidation=*/false, + /*DisableStatCache=*/false, + AllowPCHWithCompilerErrors)); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<ASTReader> @@ -657,7 +786,7 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, // source, so that declarations will be deserialized from the // AST file as needed. ASTReader *ReaderPtr = Reader.get(); - llvm::OwningPtr<ExternalASTSource> Source(Reader.take()); + OwningPtr<ExternalASTSource> Source(Reader.take()); // Unregister the cleanup for ASTReader. It will get cleaned up // by the ASTUnit cleanup. @@ -672,6 +801,7 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, AST->TheSema.reset(new Sema(PP, Context, *AST->Consumer)); AST->TheSema->Initialize(); ReaderPtr->InitializeSema(*AST->TheSema); + AST->Reader = ReaderPtr; return AST.take(); } @@ -711,22 +841,7 @@ void AddTopLevelDeclarationToHash(Decl *D, unsigned &Hash) { Hash = llvm::HashString(NameStr, Hash); } return; - } - - if (ObjCForwardProtocolDecl *Forward - = dyn_cast<ObjCForwardProtocolDecl>(D)) { - for (ObjCForwardProtocolDecl::protocol_iterator - P = Forward->protocol_begin(), - PEnd = Forward->protocol_end(); - P != PEnd; ++P) - AddTopLevelDeclarationToHash(*P, Hash); - return; - } - - if (ObjCClassDecl *Class = dyn_cast<ObjCClassDecl>(D)) { - AddTopLevelDeclarationToHash(Class->getForwardInterfaceDecl(), Hash); - return; - } + } } class TopLevelDeclTrackerConsumer : public ASTConsumer { @@ -738,24 +853,46 @@ public: : Unit(_Unit), Hash(Hash) { Hash = 0; } - - void HandleTopLevelDecl(DeclGroupRef D) { - for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) { - Decl *D = *it; - // 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<ObjCMethodDecl>(D)) - continue; - AddTopLevelDeclarationToHash(D, Hash); - Unit.addTopLevelDecl(D); + 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<ObjCMethodDecl>(D)) + return; + + AddTopLevelDeclarationToHash(D, Hash); + Unit.addTopLevelDecl(D); + + handleFileLevelDecl(D); + } + + void handleFileLevelDecl(Decl *D) { + Unit.addFileLevelDecl(D); + if (NamespaceDecl *NSD = dyn_cast<NamespaceDecl>(D)) { + for (NamespaceDecl::decl_iterator + I = NSD->decls_begin(), E = NSD->decls_end(); I != E; ++I) + handleFileLevelDecl(*I); } } + bool HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) + handleTopLevelDecl(*it); + return true; + } + // We're not interested in "interesting" decls. void HandleInterestingDecl(DeclGroupRef) {} + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) { + for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) + handleTopLevelDecl(*it); + } }; class TopLevelDeclTrackerAction : public ASTFrontendAction { @@ -787,12 +924,12 @@ class PrecompilePreambleConsumer : public PCHGenerator { public: PrecompilePreambleConsumer(ASTUnit &Unit, const Preprocessor &PP, StringRef isysroot, raw_ostream *Out) - : PCHGenerator(PP, "", /*IsModule=*/false, isysroot, Out), Unit(Unit), + : PCHGenerator(PP, "", 0, isysroot, Out), Unit(Unit), Hash(Unit.getCurrentTopLevelHashValue()) { Hash = 0; } - virtual void HandleTopLevelDecl(DeclGroupRef D) { + virtual bool HandleTopLevelDecl(DeclGroupRef D) { for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) { Decl *D = *it; // FIXME: Currently ObjC method declarations are incorrectly being @@ -804,6 +941,7 @@ public: AddTopLevelDeclarationToHash(D, Hash); TopLevelDecls.push_back(D); } + return true; } virtual void HandleTranslationUnit(ASTContext &Ctx) { @@ -852,6 +990,34 @@ public: } +static void checkAndRemoveNonDriverDiags(SmallVectorImpl<StoredDiagnostic> & + StoredDiagnostics) { + // Get rid of stored diagnostics except the ones from the driver which do not + // have a source location. + for (unsigned I = 0; I < StoredDiagnostics.size(); ++I) { + if (StoredDiagnostics[I].getLocation().isValid()) { + StoredDiagnostics.erase(StoredDiagnostics.begin()+I); + --I; + } + } +} + +static void checkAndSanitizeDiags(SmallVectorImpl<StoredDiagnostic> & + 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 (unsigned I = 0, N = StoredDiagnostics.size(); I < N; ++I) { + if (StoredDiagnostics[I].getLocation().isValid()) { + FullSourceLoc Loc(StoredDiagnostics[I].getLocation(), SM); + StoredDiagnostics[I].setLocation(Loc); + } + } +} + /// Parse the source file into a translation unit using the given compiler /// invocation, replacing the current translation unit. /// @@ -867,17 +1033,17 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { } // Create the compiler instance to use for building the AST. - llvm::OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup(Clang.get()); - llvm::IntrusiveRefCntPtr<CompilerInvocation> + IntrusiveRefCntPtr<CompilerInvocation> CCInvocation(new CompilerInvocation(*Invocation)); Clang->setInvocation(CCInvocation.getPtr()); - OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].second; + OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].File; // Set up diagnostics, capturing any diagnostics that would // otherwise be dropped. @@ -900,28 +1066,29 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { assert(Clang->getFrontendOpts().Inputs.size() == 1 && "Invocation must have exactly one source file!"); - assert(Clang->getFrontendOpts().Inputs[0].first != IK_AST && + assert(Clang->getFrontendOpts().Inputs[0].Kind != IK_AST && "FIXME: AST inputs not yet supported here!"); - assert(Clang->getFrontendOpts().Inputs[0].first != IK_LLVM_IR && + assert(Clang->getFrontendOpts().Inputs[0].Kind != IK_LLVM_IR && "IR inputs not support here!"); // Configure the various subsystems. // FIXME: Should we retain the previous file manager? + LangOpts = &Clang->getLangOpts(); FileSystemOpts = Clang->getFileSystemOpts(); FileMgr = new FileManager(FileSystemOpts); SourceMgr = new SourceManager(getDiagnostics(), *FileMgr); TheSema.reset(); Ctx = 0; PP = 0; + Reader = 0; // Clear out old caches and data. TopLevelDecls.clear(); + clearFileLevelDecls(); CleanTemporaryFiles(); if (!OverrideMainBuffer) { - StoredDiagnostics.erase( - StoredDiagnostics.begin() + NumStoredDiagnosticsFromDriver, - StoredDiagnostics.end()); + checkAndRemoveNonDriverDiags(StoredDiagnostics); TopLevelDeclsInPreamble.clear(); } @@ -934,14 +1101,12 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { // 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(); - PreprocessorOpts.DetailedRecordIncludesNestedMacroExpansions - = NestedMacroExpansions; if (OverrideMainBuffer) { PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer); PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); PreprocessorOpts.PrecompiledPreambleBytes.second = PreambleEndsAtStartOfLine; - PreprocessorOpts.ImplicitPCHInclude = PreambleFile; + PreprocessorOpts.ImplicitPCHInclude = getPreambleFile(this); PreprocessorOpts.DisablePCHValidation = true; // The stored diagnostic has the old source manager in it; update @@ -949,49 +1114,37 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { // 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 (unsigned I = NumStoredDiagnosticsFromDriver, - N = StoredDiagnostics.size(); - I < N; ++I) { - FullSourceLoc Loc(StoredDiagnostics[I].getLocation(), - getSourceManager()); - StoredDiagnostics[I].setLocation(Loc); - } + checkAndSanitizeDiags(StoredDiagnostics, getSourceManager()); // Keep track of the override buffer; SavedMainFileBuffer = OverrideMainBuffer; } - llvm::OwningPtr<TopLevelDeclTrackerAction> Act( + OwningPtr<TopLevelDeclTrackerAction> Act( new TopLevelDeclTrackerAction(*this)); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<TopLevelDeclTrackerAction> ActCleanup(Act.get()); - if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0].second, - Clang->getFrontendOpts().Inputs[0].first)) + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) goto error; if (OverrideMainBuffer) { - std::string ModName = PreambleFile; + std::string ModName = getPreambleFile(this); TranslateStoredDiagnostics(Clang->getModuleManager(), ModName, getSourceManager(), PreambleDiagnostics, StoredDiagnostics); } Act->Execute(); - - // Steal the created target, context, and preprocessor. - TheSema.reset(Clang->takeSema()); - Consumer.reset(Clang->takeASTConsumer()); - Ctx = &Clang->getASTContext(); - PP = &Clang->getPreprocessor(); - Clang->setSourceManager(0); - Clang->setFileManager(0); - Target = &Clang->getTarget(); + + transferASTDataFromCompilerInstance(*Clang); Act->EndSourceFile(); + FailedParseDiagnostics.clear(); + return false; error: @@ -1000,8 +1153,13 @@ error: delete OverrideMainBuffer; SavedMainFileBuffer = 0; } - + + // 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; } @@ -1053,7 +1211,7 @@ ASTUnit::ComputePreamble(CompilerInvocation &Invocation, // command line (to another file) or directly through the compiler invocation // (to a memory buffer). llvm::MemoryBuffer *Buffer = 0; - llvm::sys::PathWithStatus MainFilePath(FrontendOpts.Inputs[0].second); + llvm::sys::PathWithStatus MainFilePath(FrontendOpts.Inputs[0].File); if (const llvm::sys::FileStatus *MainFileStatus = MainFilePath.getFileStatus()) { // Check whether there is a file-file remapping of the main file for (PreprocessorOptions::remapped_file_iterator @@ -1103,7 +1261,7 @@ ASTUnit::ComputePreamble(CompilerInvocation &Invocation, // If the main source file was not remapped, load it now. if (!Buffer) { - Buffer = getBufferForFile(FrontendOpts.Inputs[0].second); + Buffer = getBufferForFile(FrontendOpts.Inputs[0].File); if (!Buffer) return std::make_pair((llvm::MemoryBuffer*)0, std::make_pair(0, true)); @@ -1111,7 +1269,7 @@ ASTUnit::ComputePreamble(CompilerInvocation &Invocation, } return std::make_pair(Buffer, Lexer::ComputePreamble(Buffer, - Invocation.getLangOpts(), + *Invocation.getLangOpts(), MaxLines)); } @@ -1154,7 +1312,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( bool AllowRebuild, unsigned MaxLines) { - llvm::IntrusiveRefCntPtr<CompilerInvocation> + IntrusiveRefCntPtr<CompilerInvocation> PreambleInvocation(new CompilerInvocation(PreambleInvocationIn)); FrontendOptions &FrontendOpts = PreambleInvocation->getFrontendOpts(); PreprocessorOptions &PreprocessorOpts @@ -1165,7 +1323,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( = ComputePreamble(*PreambleInvocation, MaxLines, CreatedPreambleBuffer); // If ComputePreamble() Take ownership of the preamble buffer. - llvm::OwningPtr<llvm::MemoryBuffer> OwnedPreambleBuffer; + OwningPtr<llvm::MemoryBuffer> OwnedPreambleBuffer; if (CreatedPreambleBuffer) OwnedPreambleBuffer.reset(NewPreamble.first); @@ -1173,10 +1331,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( // 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(); - if (!PreambleFile.empty()) { - llvm::sys::Path(PreambleFile).eraseFromDisk(); - PreambleFile.clear(); - } + erasePreambleFile(this); // The next time we actually see a preamble, precompile it. PreambleRebuildCounter = 1; @@ -1259,8 +1414,6 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( // Set the state of the diagnostic object to mimic its state // after parsing the preamble. - // FIXME: This won't catch any #pragma push warning changes that - // have occurred in the preamble. getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), PreambleInvocation->getDiagnosticOpts()); @@ -1270,7 +1423,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( // buffer size we reserved when creating the preamble. return CreatePaddedMainFileBuffer(NewPreamble.first, PreambleReservedSize, - FrontendOpts.Inputs[0].second); + FrontendOpts.Inputs[0].File); } } @@ -1282,7 +1435,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( // We can't reuse the previously-computed preamble. Build a new one. Preamble.clear(); PreambleDiagnostics.clear(); - llvm::sys::Path(PreambleFile).eraseFromDisk(); + erasePreambleFile(this); PreambleRebuildCounter = 1; } else if (!AllowRebuild) { // We aren't allowed to rebuild the precompiled preamble; just @@ -1323,7 +1476,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( // Save the preamble text for later; we'll need to compare against it for // subsequent reparses. - StringRef MainFilename = PreambleInvocation->getFrontendOpts().Inputs[0].second; + StringRef MainFilename = PreambleInvocation->getFrontendOpts().Inputs[0].File; Preamble.assign(FileMgr->getFile(MainFilename), NewPreamble.first->getBufferStart(), NewPreamble.first->getBufferStart() @@ -1333,7 +1486,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( delete PreambleBuffer; PreambleBuffer = llvm::MemoryBuffer::getNewUninitMemBuffer(PreambleReservedSize, - FrontendOpts.Inputs[0].second); + FrontendOpts.Inputs[0].File); memcpy(const_cast<char*>(PreambleBuffer->getBufferStart()), NewPreamble.first->getBufferStart(), Preamble.size()); memset(const_cast<char*>(PreambleBuffer->getBufferStart()) + Preamble.size(), @@ -1341,7 +1494,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( const_cast<char*>(PreambleBuffer->getBufferEnd())[-1] = '\n'; // Remap the main source file to the preamble buffer. - llvm::sys::PathWithStatus MainFilePath(FrontendOpts.Inputs[0].second); + llvm::sys::PathWithStatus MainFilePath(FrontendOpts.Inputs[0].File); PreprocessorOpts.addRemappedFile(MainFilePath.str(), PreambleBuffer); // Tell the compiler invocation to generate a temporary precompiled header. @@ -1352,14 +1505,14 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( PreprocessorOpts.PrecompiledPreambleBytes.second = false; // Create the compiler instance to use for building the precompiled preamble. - llvm::OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup(Clang.get()); Clang->setInvocation(&*PreambleInvocation); - OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].second; + OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].File; // Set up diagnostics, capturing all of the diagnostics produced. Clang->setDiagnostics(&getDiagnostics()); @@ -1385,17 +1538,15 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( assert(Clang->getFrontendOpts().Inputs.size() == 1 && "Invocation must have exactly one source file!"); - assert(Clang->getFrontendOpts().Inputs[0].first != IK_AST && + assert(Clang->getFrontendOpts().Inputs[0].Kind != IK_AST && "FIXME: AST inputs not yet supported here!"); - assert(Clang->getFrontendOpts().Inputs[0].first != IK_LLVM_IR && + assert(Clang->getFrontendOpts().Inputs[0].Kind != IK_LLVM_IR && "IR inputs not support here!"); // Clear out old caches and data. getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), Clang->getDiagnosticOpts()); - StoredDiagnostics.erase( - StoredDiagnostics.begin() + NumStoredDiagnosticsFromDriver, - StoredDiagnostics.end()); + checkAndRemoveNonDriverDiags(StoredDiagnostics); TopLevelDecls.clear(); TopLevelDeclsInPreamble.clear(); @@ -1406,10 +1557,9 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( Clang->setSourceManager(new SourceManager(getDiagnostics(), Clang->getFileManager())); - llvm::OwningPtr<PrecompilePreambleAction> Act; + OwningPtr<PrecompilePreambleAction> Act; Act.reset(new PrecompilePreambleAction(*this)); - if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0].second, - Clang->getFrontendOpts().Inputs[0].first)) { + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { llvm::sys::Path(FrontendOpts.OutputFile).eraseFromDisk(); Preamble.clear(); PreambleRebuildCounter = DefaultPreambleRebuildInterval; @@ -1438,14 +1588,11 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( // of preamble diagnostics. PreambleDiagnostics.clear(); PreambleDiagnostics.insert(PreambleDiagnostics.end(), - StoredDiagnostics.begin() + NumStoredDiagnosticsFromDriver, - StoredDiagnostics.end()); - StoredDiagnostics.erase( - StoredDiagnostics.begin() + NumStoredDiagnosticsFromDriver, - StoredDiagnostics.end()); + stored_diag_afterDriver_begin(), stored_diag_end()); + checkAndRemoveNonDriverDiags(StoredDiagnostics); // Keep track of the preamble we precompiled. - PreambleFile = FrontendOpts.OutputFile; + setPreambleFile(this, FrontendOpts.OutputFile); NumWarningsInPreamble = getDiagnostics().getNumWarnings(); // Keep track of all of the files that the source manager knows about, @@ -1480,7 +1627,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( return CreatePaddedMainFileBuffer(NewPreamble.first, PreambleReservedSize, - FrontendOpts.Inputs[0].second); + FrontendOpts.Inputs[0].File); } void ASTUnit::RealizeTopLevelDeclsFromPreamble() { @@ -1498,15 +1645,28 @@ void ASTUnit::RealizeTopLevelDeclsFromPreamble() { TopLevelDecls.insert(TopLevelDecls.begin(), Resolved.begin(), Resolved.end()); } +void ASTUnit::transferASTDataFromCompilerInstance(CompilerInstance &CI) { + // Steal the created target, context, and preprocessor. + TheSema.reset(CI.takeSema()); + Consumer.reset(CI.takeASTConsumer()); + Ctx = &CI.getASTContext(); + PP = &CI.getPreprocessor(); + CI.setSourceManager(0); + CI.setFileManager(0); + Target = &CI.getTarget(); + Reader = CI.getModuleManager(); +} + StringRef ASTUnit::getMainFileName() const { - return Invocation->getFrontendOpts().Inputs[0].second; + return Invocation->getFrontendOpts().Inputs[0].File; } ASTUnit *ASTUnit::create(CompilerInvocation *CI, - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags) { - llvm::OwningPtr<ASTUnit> AST; + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + bool CaptureDiagnostics) { + OwningPtr<ASTUnit> AST; AST.reset(new ASTUnit(false)); - ConfigureDiags(Diags, 0, 0, *AST, /*CaptureDiagnostics=*/false); + ConfigureDiags(Diags, 0, 0, *AST, CaptureDiagnostics); AST->Diagnostics = Diags; AST->Invocation = CI; AST->FileSystemOpts = CI->getFileSystemOpts(); @@ -1517,23 +1677,36 @@ ASTUnit *ASTUnit::create(CompilerInvocation *CI, } ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI, - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, ASTFrontendAction *Action, - ASTUnit *Unit) { + ASTUnit *Unit, + bool Persistent, + StringRef ResourceFilesPath, + bool OnlyLocalDecls, + bool CaptureDiagnostics, + bool PrecompilePreamble, + bool CacheCodeCompletionResults, + OwningPtr<ASTUnit> *ErrAST) { assert(CI && "A CompilerInvocation is required"); - llvm::OwningPtr<ASTUnit> OwnAST; + OwningPtr<ASTUnit> OwnAST; ASTUnit *AST = Unit; if (!AST) { // Create the AST unit. - OwnAST.reset(create(CI, Diags)); + OwnAST.reset(create(CI, Diags, CaptureDiagnostics)); AST = OwnAST.get(); } - AST->OnlyLocalDecls = false; - AST->CaptureDiagnostics = false; + if (!ResourceFilesPath.empty()) { + // Override the resources path. + CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; + } + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + if (PrecompilePreamble) + AST->PreambleRebuildCounter = 2; AST->TUKind = Action ? Action->getTranslationUnitKind() : TU_Complete; - AST->ShouldCacheCodeCompletionResults = false; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> @@ -1551,14 +1724,14 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI, AST->TargetFeatures = CI->getTargetOpts().Features; // Create the compiler instance to use for building the AST. - llvm::OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup(Clang.get()); Clang->setInvocation(CI); - AST->OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].second; + AST->OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].File; // Set up diagnostics, capturing any diagnostics that would // otherwise be dropped. @@ -1579,15 +1752,16 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI, assert(Clang->getFrontendOpts().Inputs.size() == 1 && "Invocation must have exactly one source file!"); - assert(Clang->getFrontendOpts().Inputs[0].first != IK_AST && + assert(Clang->getFrontendOpts().Inputs[0].Kind != IK_AST && "FIXME: AST inputs not yet supported here!"); - assert(Clang->getFrontendOpts().Inputs[0].first != IK_LLVM_IR && + assert(Clang->getFrontendOpts().Inputs[0].Kind != IK_LLVM_IR && "IR inputs not supported here!"); // Configure the various subsystems. AST->TheSema.reset(); AST->Ctx = 0; AST->PP = 0; + AST->Reader = 0; // Create a file manager object to provide access to and cache the filesystem. Clang->setFileManager(&AST->getFileManager()); @@ -1597,7 +1771,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI, ASTFrontendAction *Act = Action; - llvm::OwningPtr<TopLevelDeclTrackerAction> TrackerAct; + OwningPtr<TopLevelDeclTrackerAction> TrackerAct; if (!Act) { TrackerAct.reset(new TopLevelDeclTrackerAction(*AST)); Act = TrackerAct.get(); @@ -1607,21 +1781,28 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI, llvm::CrashRecoveryContextCleanupRegistrar<TopLevelDeclTrackerAction> ActCleanup(TrackerAct.get()); - if (!Act->BeginSourceFile(*Clang.get(), - Clang->getFrontendOpts().Inputs[0].second, - Clang->getFrontendOpts().Inputs[0].first)) + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { + AST->transferASTDataFromCompilerInstance(*Clang); + if (OwnAST && ErrAST) + ErrAST->swap(OwnAST); + return 0; - + } + + if (Persistent && !TrackerAct) { + Clang->getPreprocessor().addPPCallbacks( + new MacroDefinitionTrackerPPCallbacks(AST->getCurrentTopLevelHashValue())); + std::vector<ASTConsumer*> Consumers; + if (Clang->hasASTConsumer()) + Consumers.push_back(Clang->takeASTConsumer()); + Consumers.push_back(new TopLevelDeclTrackerConsumer(*AST, + AST->getCurrentTopLevelHashValue())); + Clang->setASTConsumer(new MultiplexConsumer(Consumers)); + } Act->Execute(); - + // Steal the created target, context, and preprocessor. - AST->TheSema.reset(Clang->takeSema()); - AST->Consumer.reset(Clang->takeASTConsumer()); - AST->Ctx = &Clang->getASTContext(); - AST->PP = &Clang->getPreprocessor(); - Clang->setSourceManager(0); - Clang->setFileManager(0); - AST->Target = &Clang->getTarget(); + AST->transferASTDataFromCompilerInstance(*Clang); Act->EndSourceFile(); @@ -1661,15 +1842,14 @@ bool ASTUnit::LoadFromCompilerInvocation(bool PrecompilePreamble) { } ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, bool OnlyLocalDecls, bool CaptureDiagnostics, bool PrecompilePreamble, TranslationUnitKind TUKind, - bool CacheCodeCompletionResults, - bool NestedMacroExpansions) { + bool CacheCodeCompletionResults) { // Create the AST unit. - llvm::OwningPtr<ASTUnit> AST; + OwningPtr<ASTUnit> AST; AST.reset(new ASTUnit(false)); ConfigureDiags(Diags, 0, 0, *AST, CaptureDiagnostics); AST->Diagnostics = Diags; @@ -1678,7 +1858,6 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, AST->TUKind = TUKind; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; AST->Invocation = CI; - AST->NestedMacroExpansions = NestedMacroExpansions; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> @@ -1692,7 +1871,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, const char **ArgEnd, - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, StringRef ResourceFilesPath, bool OnlyLocalDecls, bool CaptureDiagnostics, @@ -1702,7 +1881,9 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, bool PrecompilePreamble, TranslationUnitKind TUKind, bool CacheCodeCompletionResults, - bool NestedMacroExpansions) { + bool AllowPCHWithCompilerErrors, + bool SkipFunctionBodies, + OwningPtr<ASTUnit> *ErrAST) { if (!Diags.getPtr()) { // No diagnostics engine was provided, so create our own diagnostics object // with the default options. @@ -1713,7 +1894,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, SmallVector<StoredDiagnostic, 4> StoredDiagnostics; - llvm::IntrusiveRefCntPtr<CompilerInvocation> CI; + IntrusiveRefCntPtr<CompilerInvocation> CI; { @@ -1738,18 +1919,21 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, CI->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, fname); } } - CI->getPreprocessorOpts().RemappedFilesKeepOriginalName = - RemappedFilesKeepOriginalName; + PreprocessorOptions &PPOpts = CI->getPreprocessorOpts(); + PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName; + PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors; // Override the resources path. CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; + CI->getFrontendOpts().SkipFunctionBodies = SkipFunctionBodies; + // Create the AST unit. - llvm::OwningPtr<ASTUnit> AST; + OwningPtr<ASTUnit> AST; AST.reset(new ASTUnit(false)); ConfigureDiags(Diags, ArgBegin, ArgEnd, *AST, CaptureDiagnostics); AST->Diagnostics = Diags; - + Diags = 0; // Zero out now to ease cleanup during crash recovery. AST->FileSystemOpts = CI->getFileSystemOpts(); AST->FileMgr = new FileManager(AST->FileSystemOpts); AST->OnlyLocalDecls = OnlyLocalDecls; @@ -1759,24 +1943,30 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size(); AST->StoredDiagnostics.swap(StoredDiagnostics); AST->Invocation = CI; - AST->NestedMacroExpansions = NestedMacroExpansions; + CI = 0; // Zero out now to ease cleanup during crash recovery. // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> ASTUnitCleanup(AST.get()); - llvm::CrashRecoveryContextCleanupRegistrar<CompilerInvocation, - llvm::CrashRecoveryContextReleaseRefCleanup<CompilerInvocation> > - CICleanup(CI.getPtr()); - llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, - llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > - DiagCleanup(Diags.getPtr()); - return AST->LoadFromCompilerInvocation(PrecompilePreamble) ? 0 : AST.take(); + if (AST->LoadFromCompilerInvocation(PrecompilePreamble)) { + // Some error occurred, if caller wants to examine diagnostics, pass it the + // ASTUnit. + if (ErrAST) { + AST->StoredDiagnostics.swap(AST->FailedParseDiagnostics); + ErrAST->swap(AST); + } + return 0; + } + + return AST.take(); } bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { if (!Invocation) return true; + + clearFileLevelDecls(); SimpleTimer ParsingTimer(WantTiming); ParsingTimer.setOutput("Reparsing " + getMainFileName()); @@ -1808,15 +1998,15 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { // If we have a preamble file lying around, or if we might try to // build a precompiled preamble, do so now. llvm::MemoryBuffer *OverrideMainBuffer = 0; - if (!PreambleFile.empty() || PreambleRebuildCounter > 0) + if (!getPreambleFile(this).empty() || PreambleRebuildCounter > 0) OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(*Invocation); // Clear out the diagnostics state. - if (!OverrideMainBuffer) { - getDiagnostics().Reset(); - ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); - } - + getDiagnostics().Reset(); + ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); + if (OverrideMainBuffer) + getDiagnostics().setNumWarnings(NumWarningsInPreamble); + // Parse the sources bool Result = Parse(OverrideMainBuffer); @@ -1826,9 +2016,9 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { CurrentTopLevelHashValue != CompletionCacheTopLevelHashValue) CacheCodeCompletionResults(); - // We now need to clear out the completion allocator for - // clang_getCursorCompletionString; it'll be recreated if necessary. - CursorCompletionAllocator = 0; + // We now need to clear out the completion info related to this translation + // unit; it'll be recreated if necessary. + CCTUInfo.reset(); return Result; } @@ -1870,7 +2060,7 @@ namespace { | (1LL << (CodeCompletionContext::CCC_ParenthesizedExpression - 1)) | (1LL << (CodeCompletionContext::CCC_Recovery - 1)); - if (AST.getASTContext().getLangOptions().CPlusPlus) + if (AST.getASTContext().getLangOpts().CPlusPlus) NormalContexts |= (1LL << (CodeCompletionContext::CCC_EnumTag - 1)) | (1LL << (CodeCompletionContext::CCC_UnionTag - 1)) | (1LL << (CodeCompletionContext::CCC_ClassOrStructTag - 1)); @@ -1890,6 +2080,10 @@ namespace { virtual CodeCompletionAllocator &getAllocator() { return Next.getAllocator(); } + + virtual CodeCompletionTUInfo &getCodeCompletionTUInfo() { + return Next.getCodeCompletionTUInfo(); + } }; } @@ -1961,7 +2155,7 @@ static void CalculateHiddenNames(const CodeCompletionContext &Context, unsigned HiddenIDNS = (Decl::IDNS_Type | Decl::IDNS_Member | Decl::IDNS_Namespace | Decl::IDNS_Ordinary | Decl::IDNS_NonMemberOperator); - if (Ctx.getLangOptions().CPlusPlus) + if (Ctx.getLangOpts().CPlusPlus) HiddenIDNS |= Decl::IDNS_Tag; Hiding = (IDNS & HiddenIDNS); } @@ -2021,7 +2215,7 @@ void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S, if (!Context.getPreferredType().isNull()) { if (C->Kind == CXCursor_MacroDefinition) { Priority = getMacroUsagePriority(C->Completion->getTypedText(), - S.getLangOptions(), + S.getLangOpts(), Context.getPreferredType()->isAnyPointerType()); } else if (C->Type) { CanQualType Expected @@ -2047,8 +2241,8 @@ void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S, Context.getKind() == CodeCompletionContext::CCC_MacroNameUse) { // Create a new code-completion string that just contains the // macro name, without its arguments. - CodeCompletionBuilder Builder(getAllocator(), CCP_CodePattern, - C->Availability); + CodeCompletionBuilder Builder(getAllocator(), getCodeCompletionTUInfo(), + CCP_CodePattern, C->Availability); Builder.AddTypedTextChunk(C->Completion->getTypedText()); CursorKind = CXCursor_NotImplemented; Priority = CCP_CodePattern; @@ -2089,7 +2283,7 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, CompletionTimer.setOutput("Code completion @ " + File + ":" + Twine(Line) + ":" + Twine(Column)); - llvm::IntrusiveRefCntPtr<CompilerInvocation> + IntrusiveRefCntPtr<CompilerInvocation> CCInvocation(new CompilerInvocation(*Invocation)); FrontendOptions &FrontendOpts = CCInvocation->getFrontendOpts(); @@ -2105,16 +2299,16 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, FrontendOpts.CodeCompletionAt.Column = Column; // Set the language options appropriately. - LangOpts = CCInvocation->getLangOpts(); + LangOpts = *CCInvocation->getLangOpts(); - llvm::OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup(Clang.get()); Clang->setInvocation(&*CCInvocation); - OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].second; + OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].File; // Set up diagnostics, capturing any diagnostics produced. Clang->setDiagnostics(&Diag); @@ -2140,9 +2334,9 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, assert(Clang->getFrontendOpts().Inputs.size() == 1 && "Invocation must have exactly one source file!"); - assert(Clang->getFrontendOpts().Inputs[0].first != IK_AST && + assert(Clang->getFrontendOpts().Inputs[0].Kind != IK_AST && "FIXME: AST inputs not yet supported here!"); - assert(Clang->getFrontendOpts().Inputs[0].first != IK_LLVM_IR && + assert(Clang->getFrontendOpts().Inputs[0].Kind != IK_LLVM_IR && "IR inputs not support here!"); @@ -2174,12 +2368,14 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, FrontendOpts.ShowGlobalSymbolsInCodeCompletion); Clang->setCodeCompletionConsumer(AugmentedConsumer); + Clang->getFrontendOpts().SkipFunctionBodies = true; + // 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. llvm::MemoryBuffer *OverrideMainBuffer = 0; - if (!PreambleFile.empty()) { + if (!getPreambleFile(this).empty()) { using llvm::sys::FileStatus; llvm::sys::PathWithStatus CompleteFilePath(File); llvm::sys::PathWithStatus MainPath(OriginalSourceFile); @@ -2196,14 +2392,14 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, // make that override happen and introduce the preamble. PreprocessorOpts.DisableStatCache = true; StoredDiagnostics.insert(StoredDiagnostics.end(), - this->StoredDiagnostics.begin(), - this->StoredDiagnostics.begin() + NumStoredDiagnosticsFromDriver); + stored_diag_begin(), + stored_diag_afterDriver_begin()); if (OverrideMainBuffer) { PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer); PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); PreprocessorOpts.PrecompiledPreambleBytes.second = PreambleEndsAtStartOfLine; - PreprocessorOpts.ImplicitPCHInclude = PreambleFile; + PreprocessorOpts.ImplicitPCHInclude = getPreambleFile(this); PreprocessorOpts.DisablePCHValidation = true; OwnedBuffers.push_back(OverrideMainBuffer); @@ -2215,12 +2411,11 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, // Disable the preprocessing record PreprocessorOpts.DetailedRecord = false; - llvm::OwningPtr<SyntaxOnlyAction> Act; + OwningPtr<SyntaxOnlyAction> Act; Act.reset(new SyntaxOnlyAction); - if (Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0].second, - Clang->getFrontendOpts().Inputs[0].first)) { + if (Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { if (OverrideMainBuffer) { - std::string ModName = PreambleFile; + std::string ModName = getPreambleFile(this); TranslateStoredDiagnostics(Clang->getModuleManager(), ModName, getSourceManager(), PreambleDiagnostics, StoredDiagnostics); @@ -2228,15 +2423,14 @@ void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, Act->Execute(); Act->EndSourceFile(); } + + checkAndSanitizeDiags(StoredDiagnostics, getSourceManager()); } CXSaveError ASTUnit::Save(StringRef File) { - if (getDiagnostics().hasUnrecoverableErrorOccurred()) - return CXSaveError_TranslationErrors; - // Write to a temporary file and later rename it to the actual file, to avoid // possible race conditions. - llvm::SmallString<128> TempPath; + SmallString<128> TempPath; TempPath = File; TempPath += "-%%%%%%%%"; int fd; @@ -2250,10 +2444,12 @@ CXSaveError ASTUnit::Save(StringRef File) { serialize(Out); Out.close(); - if (Out.has_error()) + if (Out.has_error()) { + Out.clear_error(); return CXSaveError_Unknown; + } - if (llvm::error_code ec = llvm::sys::fs::rename(TempPath.str(), File)) { + if (llvm::sys::fs::rename(TempPath.str(), File)) { bool exists; llvm::sys::fs::remove(TempPath.str(), exists); return CXSaveError_Unknown; @@ -2263,14 +2459,13 @@ CXSaveError ASTUnit::Save(StringRef File) { } bool ASTUnit::serialize(raw_ostream &OS) { - if (getDiagnostics().hasErrorOccurred()) - return true; + bool hasErrors = getDiagnostics().hasErrorOccurred(); - std::vector<unsigned char> Buffer; + SmallString<128> Buffer; llvm::BitstreamWriter Stream(Buffer); ASTWriter Writer(Stream); // FIXME: Handle modules - Writer.WriteAST(getSema(), 0, std::string(), /*IsModule=*/false, ""); + Writer.WriteAST(getSema(), 0, std::string(), 0, "", hasErrors); // Write the generated bitstream to "Out". if (!Buffer.empty()) @@ -2303,7 +2498,7 @@ void ASTUnit::TranslateStoredDiagnostics( SmallVector<StoredDiagnostic, 4> Result; Result.reserve(Diags.size()); assert(MMan && "Don't have a module manager"); - serialization::Module *Mod = MMan->ModuleMgr.lookup(ModName); + serialization::ModuleFile *Mod = MMan->ModuleMgr.lookup(ModName); assert(Mod && "Don't have preamble module"); SLocRemap &Remap = Mod->SLocRemap; for (unsigned I = 0, N = Diags.size(); I != N; ++I) { @@ -2347,6 +2542,95 @@ void ASTUnit::TranslateStoredDiagnostics( Result.swap(Out); } +static inline bool compLocDecl(std::pair<unsigned, Decl *> L, + std::pair<unsigned, Decl *> R) { + return L.first < R.first; +} + +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; + llvm::tie(FID, Offset) = SM.getDecomposedLoc(FileLoc); + if (FID.isInvalid()) + return; + + LocDeclsTy *&Decls = FileDecls[FID]; + if (!Decls) + Decls = new LocDeclsTy(); + + std::pair<unsigned, Decl *> 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, compLocDecl); + + Decls->insert(I, LocDecl); +} + +void ASTUnit::findFileRegionDecls(FileID File, unsigned Offset, unsigned Length, + SmallVectorImpl<Decl *> &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*)0), compLocDecl); + 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*)0), + compLocDecl); + 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(); @@ -2403,6 +2687,50 @@ SourceLocation ASTUnit::mapLocationToPreamble(SourceLocation Loc) { 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); +} + void ASTUnit::PreambleData::countLines() const { NumLines = 0; if (empty()) diff --git a/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp b/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp index 8195445..58a6b8d 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp @@ -467,7 +467,7 @@ void PTHWriter::GeneratePTH(const std::string &MainFile) { // Iterate over all the files in SourceManager. Create a lexer // for each file and cache the tokens. SourceManager &SM = PP.getSourceManager(); - const LangOptions &LOpts = PP.getLangOptions(); + const LangOptions &LOpts = PP.getLangOpts(); for (SourceManager::fileinfo_iterator I = SM.fileinfo_begin(), E = SM.fileinfo_end(); I != E; ++I) { @@ -540,7 +540,7 @@ void clang::CacheTokens(Preprocessor &PP, llvm::raw_fd_ostream* OS) { // Get the name of the main file. const SourceManager &SrcMgr = PP.getSourceManager(); const FileEntry *MainFile = SrcMgr.getFileEntryForID(SrcMgr.getMainFileID()); - llvm::SmallString<128> MainFilePath(MainFile->getName()); + SmallString<128> MainFilePath(MainFile->getName()); llvm::sys::fs::make_absolute(MainFilePath); diff --git a/contrib/llvm/tools/clang/lib/Frontend/ChainedDiagnosticConsumer.cpp b/contrib/llvm/tools/clang/lib/Frontend/ChainedDiagnosticConsumer.cpp new file mode 100644 index 0000000..c1d3db8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ChainedDiagnosticConsumer.cpp @@ -0,0 +1,14 @@ +//===- ChainedDiagnosticConsumer.cpp - Chain Diagnostic Clients -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ChainedDiagnosticConsumer.h" + +using namespace clang; + +void ChainedDiagnosticConsumer::anchor() { } diff --git a/contrib/llvm/tools/clang/lib/Frontend/ChainedIncludesSource.cpp b/contrib/llvm/tools/clang/lib/Frontend/ChainedIncludesSource.cpp new file mode 100644 index 0000000..dbb06bd --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ChainedIncludesSource.cpp @@ -0,0 +1,240 @@ +//===- ChainedIncludesSource.cpp - Chained PCHs in Memory -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ChainedIncludesSource class, which converts headers +// to chained PCHs in memory, mainly used for testing. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ChainedIncludesSource.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/TargetInfo.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace clang; + +static ASTReader *createASTReader(CompilerInstance &CI, + StringRef pchFile, + SmallVector<llvm::MemoryBuffer *, 4> &memBufs, + SmallVector<std::string, 4> &bufNames, + ASTDeserializationListener *deserialListener = 0) { + Preprocessor &PP = CI.getPreprocessor(); + OwningPtr<ASTReader> Reader; + Reader.reset(new ASTReader(PP, CI.getASTContext(), /*isysroot=*/"", + /*DisableValidation=*/true)); + for (unsigned ti = 0; ti < bufNames.size(); ++ti) { + StringRef sr(bufNames[ti]); + Reader->addInMemoryBuffer(sr, memBufs[ti]); + } + Reader->setDeserializationListener(deserialListener); + switch (Reader->ReadAST(pchFile, serialization::MK_PCH)) { + case ASTReader::Success: + // Set the predefines buffer as suggested by the PCH reader. + PP.setPredefines(Reader->getSuggestedPredefines()); + return Reader.take(); + + case ASTReader::Failure: + case ASTReader::IgnorePCH: + break; + } + return 0; +} + +ChainedIncludesSource::~ChainedIncludesSource() { + for (unsigned i = 0, e = CIs.size(); i != e; ++i) + delete CIs[i]; +} + +ChainedIncludesSource *ChainedIncludesSource::create(CompilerInstance &CI) { + + std::vector<std::string> &includes = CI.getPreprocessorOpts().ChainedIncludes; + assert(!includes.empty() && "No '-chain-include' in options!"); + + OwningPtr<ChainedIncludesSource> source(new ChainedIncludesSource()); + InputKind IK = CI.getFrontendOpts().Inputs[0].Kind; + + SmallVector<llvm::MemoryBuffer *, 4> serialBufs; + SmallVector<std::string, 4> serialBufNames; + + for (unsigned i = 0, e = includes.size(); i != e; ++i) { + bool firstInclude = (i == 0); + OwningPtr<CompilerInvocation> CInvok; + CInvok.reset(new CompilerInvocation(CI.getInvocation())); + + CInvok->getPreprocessorOpts().ChainedIncludes.clear(); + CInvok->getPreprocessorOpts().ImplicitPCHInclude.clear(); + CInvok->getPreprocessorOpts().ImplicitPTHInclude.clear(); + CInvok->getPreprocessorOpts().DisablePCHValidation = true; + CInvok->getPreprocessorOpts().Includes.clear(); + CInvok->getPreprocessorOpts().MacroIncludes.clear(); + CInvok->getPreprocessorOpts().Macros.clear(); + + CInvok->getFrontendOpts().Inputs.clear(); + CInvok->getFrontendOpts().Inputs.push_back(FrontendInputFile(includes[i], + IK)); + + TextDiagnosticPrinter *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), DiagnosticOptions()); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + new DiagnosticsEngine(DiagID, DiagClient)); + + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + Clang->setInvocation(CInvok.take()); + Clang->setDiagnostics(Diags.getPtr()); + Clang->setTarget(TargetInfo::CreateTargetInfo(Clang->getDiagnostics(), + Clang->getTargetOpts())); + Clang->createFileManager(); + Clang->createSourceManager(Clang->getFileManager()); + Clang->createPreprocessor(); + Clang->getDiagnosticClient().BeginSourceFile(Clang->getLangOpts(), + &Clang->getPreprocessor()); + Clang->createASTContext(); + + SmallVector<char, 256> serialAST; + llvm::raw_svector_ostream OS(serialAST); + OwningPtr<ASTConsumer> consumer; + consumer.reset(new PCHGenerator(Clang->getPreprocessor(), "-", 0, + /*isysroot=*/"", &OS)); + Clang->getASTContext().setASTMutationListener( + consumer->GetASTMutationListener()); + Clang->setASTConsumer(consumer.take()); + Clang->createSema(TU_Prefix, 0); + + if (firstInclude) { + Preprocessor &PP = Clang->getPreprocessor(); + PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(), + PP.getLangOpts()); + } else { + assert(!serialBufs.empty()); + SmallVector<llvm::MemoryBuffer *, 4> bufs; + for (unsigned si = 0, se = serialBufs.size(); si != se; ++si) { + bufs.push_back(llvm::MemoryBuffer::getMemBufferCopy( + StringRef(serialBufs[si]->getBufferStart(), + serialBufs[si]->getBufferSize()))); + } + std::string pchName = includes[i-1]; + llvm::raw_string_ostream os(pchName); + os << ".pch" << i-1; + os.flush(); + + serialBufNames.push_back(pchName); + + OwningPtr<ExternalASTSource> Reader; + + Reader.reset(createASTReader(*Clang, pchName, bufs, serialBufNames, + Clang->getASTConsumer().GetASTDeserializationListener())); + if (!Reader) + return 0; + Clang->getASTContext().setExternalSource(Reader); + } + + if (!Clang->InitializeSourceManager(includes[i])) + return 0; + + ParseAST(Clang->getSema()); + OS.flush(); + Clang->getDiagnosticClient().EndSourceFile(); + serialBufs.push_back( + llvm::MemoryBuffer::getMemBufferCopy(StringRef(serialAST.data(), + serialAST.size()))); + source->CIs.push_back(Clang.take()); + } + + assert(!serialBufs.empty()); + std::string pchName = includes.back() + ".pch-final"; + serialBufNames.push_back(pchName); + OwningPtr<ASTReader> Reader; + Reader.reset(createASTReader(CI, pchName, serialBufs, serialBufNames)); + if (!Reader) + return 0; + + source->FinalReader.reset(Reader.take()); + return source.take(); +} + +//===----------------------------------------------------------------------===// +// ExternalASTSource interface. +//===----------------------------------------------------------------------===// + +Decl *ChainedIncludesSource::GetExternalDecl(uint32_t ID) { + return getFinalReader().GetExternalDecl(ID); +} +Selector ChainedIncludesSource::GetExternalSelector(uint32_t ID) { + return getFinalReader().GetExternalSelector(ID); +} +uint32_t ChainedIncludesSource::GetNumExternalSelectors() { + return getFinalReader().GetNumExternalSelectors(); +} +Stmt *ChainedIncludesSource::GetExternalDeclStmt(uint64_t Offset) { + return getFinalReader().GetExternalDeclStmt(Offset); +} +CXXBaseSpecifier * +ChainedIncludesSource::GetExternalCXXBaseSpecifiers(uint64_t Offset) { + return getFinalReader().GetExternalCXXBaseSpecifiers(Offset); +} +DeclContextLookupResult +ChainedIncludesSource::FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) { + return getFinalReader().FindExternalVisibleDeclsByName(DC, Name); +} +ExternalLoadResult +ChainedIncludesSource::FindExternalLexicalDecls(const DeclContext *DC, + bool (*isKindWeWant)(Decl::Kind), + SmallVectorImpl<Decl*> &Result) { + return getFinalReader().FindExternalLexicalDecls(DC, isKindWeWant, Result); +} +void ChainedIncludesSource::CompleteType(TagDecl *Tag) { + return getFinalReader().CompleteType(Tag); +} +void ChainedIncludesSource::CompleteType(ObjCInterfaceDecl *Class) { + return getFinalReader().CompleteType(Class); +} +void ChainedIncludesSource::StartedDeserializing() { + return getFinalReader().StartedDeserializing(); +} +void ChainedIncludesSource::FinishedDeserializing() { + return getFinalReader().FinishedDeserializing(); +} +void ChainedIncludesSource::StartTranslationUnit(ASTConsumer *Consumer) { + return getFinalReader().StartTranslationUnit(Consumer); +} +void ChainedIncludesSource::PrintStats() { + return getFinalReader().PrintStats(); +} +void ChainedIncludesSource::getMemoryBufferSizes(MemoryBufferSizes &sizes)const{ + for (unsigned i = 0, e = CIs.size(); i != e; ++i) { + if (const ExternalASTSource *eSrc = + CIs[i]->getASTContext().getExternalSource()) { + eSrc->getMemoryBufferSizes(sizes); + } + } + + getFinalReader().getMemoryBufferSizes(sizes); +} + +void ChainedIncludesSource::InitializeSema(Sema &S) { + return getFinalReader().InitializeSema(S); +} +void ChainedIncludesSource::ForgetSema() { + return getFinalReader().ForgetSema(); +} +void ChainedIncludesSource::ReadMethodPool(Selector Sel) { + getFinalReader().ReadMethodPool(Sel); +} +bool ChainedIncludesSource::LookupUnqualified(LookupResult &R, Scope *S) { + return getFinalReader().LookupUnqualified(R, S); +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp index 5526487..cab6b90 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp @@ -11,6 +11,7 @@ #include "clang/Sema/Sema.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" @@ -24,6 +25,7 @@ #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/LogDiagnosticPrinter.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/VerifyDiagnosticConsumer.h" #include "clang/Frontend/Utils.h" @@ -35,6 +37,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Support/Timer.h" #include "llvm/Support/Host.h" +#include "llvm/Support/LockFileManager.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" @@ -42,18 +45,6 @@ #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Config/config.h" -// Support for FileLockManager -#include <fstream> -#include <sys/types.h> -#include <sys/stat.h> - -#if LLVM_ON_WIN32 -#include <windows.h> -#endif -#if LLVM_ON_UNIX -#include <unistd.h> -#endif - using namespace clang; CompilerInstance::CompilerInstance() @@ -97,6 +88,7 @@ void CompilerInstance::setASTConsumer(ASTConsumer *Value) { void CompilerInstance::setCodeCompletionConsumer(CodeCompleteConsumer *Value) { CompletionConsumer.reset(Value); + getFrontendOpts().SkipFunctionBodies = Value != 0; } // Diagnostics @@ -104,7 +96,7 @@ static void SetUpBuildDumpLog(const DiagnosticOptions &DiagOpts, unsigned argc, const char* const *argv, DiagnosticsEngine &Diags) { std::string ErrorInfo; - llvm::OwningPtr<raw_ostream> OS( + OwningPtr<raw_ostream> OS( new llvm::raw_fd_ostream(DiagOpts.DumpBuildInformation.c_str(), ErrorInfo)); if (!ErrorInfo.empty()) { Diags.Report(diag::err_fe_unable_to_open_logfile) @@ -153,6 +145,28 @@ static void SetUpDiagnosticLog(const DiagnosticOptions &DiagOpts, Diags.setClient(new ChainedDiagnosticConsumer(Diags.takeClient(), Logger)); } +static void SetupSerializedDiagnostics(const DiagnosticOptions &DiagOpts, + DiagnosticsEngine &Diags, + StringRef OutputFile) { + std::string ErrorInfo; + OwningPtr<llvm::raw_fd_ostream> OS; + OS.reset(new llvm::raw_fd_ostream(OutputFile.str().c_str(), ErrorInfo, + llvm::raw_fd_ostream::F_Binary)); + + if (!ErrorInfo.empty()) { + Diags.Report(diag::warn_fe_serialized_diag_failure) + << OutputFile << ErrorInfo; + return; + } + + DiagnosticConsumer *SerializedConsumer = + clang::serialized_diags::create(OS.take(), DiagOpts); + + + Diags.setClient(new ChainedDiagnosticConsumer(Diags.takeClient(), + SerializedConsumer)); +} + void CompilerInstance::createDiagnostics(int Argc, const char* const *Argv, DiagnosticConsumer *Client, bool ShouldOwnClient, @@ -162,15 +176,15 @@ void CompilerInstance::createDiagnostics(int Argc, const char* const *Argv, &getCodeGenOpts()); } -llvm::IntrusiveRefCntPtr<DiagnosticsEngine> +IntrusiveRefCntPtr<DiagnosticsEngine> CompilerInstance::createDiagnostics(const DiagnosticOptions &Opts, int Argc, const char* const *Argv, DiagnosticConsumer *Client, bool ShouldOwnClient, bool ShouldCloneClient, const CodeGenOptions *CodeGenOpts) { - llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags(new DiagnosticsEngine(DiagID)); // Create the diagnostic client for reporting errors or for @@ -194,6 +208,10 @@ CompilerInstance::createDiagnostics(const DiagnosticOptions &Opts, if (!Opts.DumpBuildInformation.empty()) SetUpBuildDumpLog(Opts, Argc, Argv, *Diags); + if (!Opts.DiagnosticSerializationFile.empty()) + SetupSerializedDiagnostics(Opts, *Diags, + Opts.DiagnosticSerializationFile); + // Configure our handling of diagnostics. ProcessWarningOptions(*Diags, Opts); @@ -223,7 +241,10 @@ void CompilerInstance::createPreprocessor() { PTHMgr = PTHManager::Create(PPOpts.TokenCache, getDiagnostics()); // Create the Preprocessor. - HeaderSearch *HeaderInfo = new HeaderSearch(getFileManager()); + HeaderSearch *HeaderInfo = new HeaderSearch(getFileManager(), + getDiagnostics(), + getLangOpts(), + &getTarget()); PP = new Preprocessor(getDiagnostics(), getLangOpts(), &getTarget(), getSourceManager(), *HeaderInfo, *this, PTHMgr, /*OwnsHeaderSearch=*/true); @@ -237,28 +258,28 @@ void CompilerInstance::createPreprocessor() { } if (PPOpts.DetailedRecord) - PP->createPreprocessingRecord( - PPOpts.DetailedRecordIncludesNestedMacroExpansions); + PP->createPreprocessingRecord(PPOpts.DetailedRecordConditionalDirectives); InitializePreprocessor(*PP, PPOpts, getHeaderSearchOpts(), getFrontendOpts()); // Set up the module path, including the hash for the // module-creation options. - llvm::SmallString<256> SpecificModuleCache( + SmallString<256> SpecificModuleCache( getHeaderSearchOpts().ModuleCachePath); if (!getHeaderSearchOpts().DisableModuleHash) llvm::sys::path::append(SpecificModuleCache, getInvocation().getModuleHash()); - PP->getHeaderSearchInfo().configureModules(SpecificModuleCache, - getPreprocessorOpts().ModuleBuildPath.empty() - ? std::string() - : getPreprocessorOpts().ModuleBuildPath.back()); + PP->getHeaderSearchInfo().setModuleCachePath(SpecificModuleCache); // Handle generating dependencies, if requested. const DependencyOutputOptions &DepOpts = getDependencyOutputOpts(); if (!DepOpts.OutputFile.empty()) AttachDependencyFileGen(*PP, DepOpts); + if (!DepOpts.DOTOutputFile.empty()) + AttachDependencyGraphGen(*PP, DepOpts.DOTOutputFile, + getHeaderSearchOpts().Sysroot); + // Handle generating header include information, if requested. if (DepOpts.ShowHeaderIncludes) AttachHeaderIncludeGen(*PP); @@ -286,12 +307,14 @@ void CompilerInstance::createASTContext() { void CompilerInstance::createPCHExternalASTSource(StringRef Path, bool DisablePCHValidation, bool DisableStatCache, + bool AllowPCHWithCompilerErrors, void *DeserializationListener){ - llvm::OwningPtr<ExternalASTSource> Source; + OwningPtr<ExternalASTSource> Source; bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; Source.reset(createPCHExternalASTSource(Path, getHeaderSearchOpts().Sysroot, DisablePCHValidation, DisableStatCache, + AllowPCHWithCompilerErrors, getPreprocessor(), getASTContext(), DeserializationListener, Preamble)); @@ -304,14 +327,16 @@ CompilerInstance::createPCHExternalASTSource(StringRef Path, const std::string &Sysroot, bool DisablePCHValidation, bool DisableStatCache, + bool AllowPCHWithCompilerErrors, Preprocessor &PP, ASTContext &Context, void *DeserializationListener, bool Preamble) { - llvm::OwningPtr<ASTReader> Reader; + OwningPtr<ASTReader> Reader; Reader.reset(new ASTReader(PP, Context, Sysroot.empty() ? "" : Sysroot.c_str(), - DisablePCHValidation, DisableStatCache)); + DisablePCHValidation, DisableStatCache, + AllowPCHWithCompilerErrors)); Reader->setDeserializationListener( static_cast<ASTDeserializationListener *>(DeserializationListener)); @@ -359,7 +384,7 @@ static bool EnableCodeCompletion(Preprocessor &PP, void CompilerInstance::createCodeCompletionConsumer() { const ParsedSourceLocation &Loc = getFrontendOpts().CodeCompletionAt; if (!CompletionConsumer) { - CompletionConsumer.reset( + setCodeCompletionConsumer( createCodeCompletionConsumer(getPreprocessor(), Loc.FileName, Loc.Line, Loc.Column, getFrontendOpts().ShowMacrosInCodeCompletion, @@ -370,14 +395,14 @@ void CompilerInstance::createCodeCompletionConsumer() { return; } else if (EnableCodeCompletion(getPreprocessor(), Loc.FileName, Loc.Line, Loc.Column)) { - CompletionConsumer.reset(); + setCodeCompletionConsumer(0); return; } if (CompletionConsumer->isOutputBinary() && llvm::sys::Program::ChangeStdoutToBinary()) { getPreprocessor().getDiagnostics().Report(diag::err_fe_stdout_binary); - CompletionConsumer.reset(); + setCodeCompletionConsumer(0); } } @@ -424,7 +449,7 @@ void CompilerInstance::clearOutputFiles(bool EraseFiles) { bool existed; llvm::sys::fs::remove(it->TempFilename, existed); } else { - llvm::SmallString<128> NewOutFile(it->Filename); + SmallString<128> NewOutFile(it->Filename); // If '-working-directory' was passed, the output filename should be // relative to that. @@ -450,7 +475,8 @@ CompilerInstance::createDefaultOutputFile(bool Binary, StringRef InFile, StringRef Extension) { return createOutputFile(getFrontendOpts().OutputFile, Binary, - /*RemoveFileOnSignal=*/true, InFile, Extension); + /*RemoveFileOnSignal=*/true, InFile, Extension, + /*UseTemporary=*/true); } llvm::raw_fd_ostream * @@ -458,12 +484,14 @@ CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary, bool RemoveFileOnSignal, StringRef InFile, StringRef Extension, - bool UseTemporary) { + bool UseTemporary, + bool CreateMissingDirectories) { std::string Error, OutputPathName, TempPathName; llvm::raw_fd_ostream *OS = createOutputFile(OutputPath, Error, Binary, RemoveFileOnSignal, InFile, Extension, UseTemporary, + CreateMissingDirectories, &OutputPathName, &TempPathName); if (!OS) { @@ -488,8 +516,12 @@ CompilerInstance::createOutputFile(StringRef OutputPath, StringRef InFile, StringRef Extension, bool UseTemporary, + bool CreateMissingDirectories, std::string *ResultPathName, std::string *TempPathName) { + assert((!CreateMissingDirectories || UseTemporary) && + "CreateMissingDirectories is only allowed when using temporary files"); + std::string OutFile, TempFile; if (!OutputPath.empty()) { OutFile = OutputPath; @@ -504,18 +536,26 @@ CompilerInstance::createOutputFile(StringRef OutputPath, OutFile = "-"; } - llvm::OwningPtr<llvm::raw_fd_ostream> OS; + OwningPtr<llvm::raw_fd_ostream> OS; std::string OSFile; if (UseTemporary && OutFile != "-") { - llvm::sys::Path OutPath(OutFile); - // Only create the temporary if we can actually write to OutPath, otherwise - // we want to fail early. + // Only create the temporary if the parent directory exists (or create + // missing directories is true) and we can actually write to OutPath, + // otherwise we want to fail early. + SmallString<256> AbsPath(OutputPath); + llvm::sys::fs::make_absolute(AbsPath); + llvm::sys::Path OutPath(AbsPath); + bool ParentExists = false; + if (llvm::sys::fs::exists(llvm::sys::path::parent_path(AbsPath.str()), + ParentExists)) + ParentExists = false; bool Exists; - if ((llvm::sys::fs::exists(OutPath.str(), Exists) || !Exists) || - (OutPath.isRegularFile() && OutPath.canWrite())) { + if ((CreateMissingDirectories || ParentExists) && + ((llvm::sys::fs::exists(AbsPath.str(), Exists) || !Exists) || + (OutPath.isRegularFile() && OutPath.canWrite()))) { // Create a temporary file. - llvm::SmallString<128> TempPath; + SmallString<128> TempPath; TempPath = OutFile; TempPath += "-%%%%%%%%"; int fd; @@ -550,12 +590,15 @@ CompilerInstance::createOutputFile(StringRef OutputPath, // Initialization Utilities -bool CompilerInstance::InitializeSourceManager(StringRef InputFile) { - return InitializeSourceManager(InputFile, getDiagnostics(), getFileManager(), - getSourceManager(), getFrontendOpts()); +bool CompilerInstance::InitializeSourceManager(StringRef InputFile, + SrcMgr::CharacteristicKind Kind){ + return InitializeSourceManager(InputFile, Kind, getDiagnostics(), + getFileManager(), getSourceManager(), + getFrontendOpts()); } bool CompilerInstance::InitializeSourceManager(StringRef InputFile, + SrcMgr::CharacteristicKind Kind, DiagnosticsEngine &Diags, FileManager &FileMgr, SourceManager &SourceMgr, @@ -567,9 +610,9 @@ bool CompilerInstance::InitializeSourceManager(StringRef InputFile, Diags.Report(diag::err_fe_error_reading) << InputFile; return false; } - SourceMgr.createMainFileID(File); + SourceMgr.createMainFileID(File, Kind); } else { - llvm::OwningPtr<llvm::MemoryBuffer> SB; + OwningPtr<llvm::MemoryBuffer> SB; if (llvm::MemoryBuffer::getSTDIN(SB)) { // FIXME: Give ec.message() in this diag. Diags.Report(diag::err_fe_error_reading_stdin); @@ -577,7 +620,7 @@ bool CompilerInstance::InitializeSourceManager(StringRef InputFile, } const FileEntry *File = FileMgr.getVirtualFile(SB->getBufferIdentifier(), SB->getBufferSize(), 0); - SourceMgr.createMainFileID(File); + SourceMgr.createMainFileID(File, Kind); SourceMgr.overrideFileContents(File, SB.take()); } @@ -612,7 +655,7 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { if (getHeaderSearchOpts().Verbose) OS << "clang -cc1 version " CLANG_VERSION_STRING << " based upon " << PACKAGE_STRING - << " hosted on " << llvm::sys::getHostTriple() << "\n"; + << " default target " << llvm::sys::getDefaultTargetTriple() << "\n"; if (getFrontendOpts().ShowTimers) createFrontendTimer(); @@ -621,18 +664,19 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { llvm::EnableStatistics(); for (unsigned i = 0, e = getFrontendOpts().Inputs.size(); i != e; ++i) { - const std::string &InFile = getFrontendOpts().Inputs[i].second; - // Reset the ID tables if we are reusing the SourceManager. if (hasSourceManager()) getSourceManager().clearIDTables(); - if (Act.BeginSourceFile(*this, InFile, getFrontendOpts().Inputs[i].first)) { + if (Act.BeginSourceFile(*this, getFrontendOpts().Inputs[i])) { Act.Execute(); Act.EndSourceFile(); } } + // Notify the diagnostic client that all files were processed. + getDiagnostics().getClient()->finish(); + if (getDiagnosticOpts().ShowCarets) { // We can have multiple diagnostics sharing one diagnostic client. // Get the total number of warnings/errors from the client. @@ -670,320 +714,105 @@ static InputKind getSourceInputKindFromOptions(const LangOptions &LangOpts) { } namespace { - struct CompileModuleData { + struct CompileModuleMapData { CompilerInstance &Instance; - GeneratePCHAction &CreateModuleAction; + GenerateModuleAction &CreateModuleAction; }; } /// \brief Helper function that executes the module-generating action under /// a crash recovery context. -static void doCompileModule(void *UserData) { - CompileModuleData &Data = *reinterpret_cast<CompileModuleData *>(UserData); +static void doCompileMapModule(void *UserData) { + CompileModuleMapData &Data + = *reinterpret_cast<CompileModuleMapData *>(UserData); Data.Instance.ExecuteAction(Data.CreateModuleAction); } -namespace { - /// \brief Class that manages the creation of a lock file to aid - /// implicit coordination between different processes. - /// - /// The implicit coordination works by creating a ".lock" file alongside - /// the file that we're coordinating for, using the atomicity of the file - /// system to ensure that only a single process can create that ".lock" file. - /// When the lock file is removed, the owning process has finished the - /// operation. - class LockFileManager { - public: - /// \brief Describes the state of a lock file. - enum LockFileState { - /// \brief The lock file has been created and is owned by this instance - /// of the object. - LFS_Owned, - /// \brief The lock file already exists and is owned by some other - /// instance. - LFS_Shared, - /// \brief An error occurred while trying to create or find the lock - /// file. - LFS_Error - }; - - private: - llvm::SmallString<128> LockFileName; - llvm::SmallString<128> UniqueLockFileName; - - llvm::Optional<std::pair<std::string, int> > Owner; - llvm::Optional<llvm::error_code> Error; - - LockFileManager(const LockFileManager &); - LockFileManager &operator=(const LockFileManager &); - - static llvm::Optional<std::pair<std::string, int> > - readLockFile(StringRef LockFileName); - - static bool processStillExecuting(StringRef Hostname, int PID); - - public: - - LockFileManager(StringRef FileName); - ~LockFileManager(); - - /// \brief Determine the state of the lock file. - LockFileState getState() const; - - operator LockFileState() const { return getState(); } - - /// \brief For a shared lock, wait until the owner releases the lock. - void waitForUnlock(); - }; -} - -/// \brief Attempt to read the lock file with the given name, if it exists. -/// -/// \param LockFileName The name of the lock file to read. -/// -/// \returns The process ID of the process that owns this lock file -llvm::Optional<std::pair<std::string, int> > -LockFileManager::readLockFile(StringRef LockFileName) { - // Check whether the lock file exists. If not, clearly there's nothing - // to read, so we just return. - bool Exists = false; - if (llvm::sys::fs::exists(LockFileName, Exists) || !Exists) - return llvm::Optional<std::pair<std::string, int> >(); - - // Read the owning host and PID out of the lock file. If it appears that the - // owning process is dead, the lock file is invalid. - int PID = 0; - std::string Hostname; - std::ifstream Input(LockFileName.str().c_str()); - if (Input >> Hostname >> PID && PID > 0 && - processStillExecuting(Hostname, PID)) - return std::make_pair(Hostname, PID); - - // Delete the lock file. It's invalid anyway. - bool Existed; - llvm::sys::fs::remove(LockFileName, Existed); - return llvm::Optional<std::pair<std::string, int> >(); -} - -bool LockFileManager::processStillExecuting(StringRef Hostname, int PID) { -#if LLVM_ON_UNIX - char MyHostname[256]; - MyHostname[255] = 0; - MyHostname[0] = 0; - gethostname(MyHostname, 255); - // Check whether the process is dead. If so, we're done. - if (MyHostname == Hostname && getsid(PID) == -1 && errno == ESRCH) - return false; -#endif - - return true; -} - -LockFileManager::LockFileManager(StringRef FileName) -{ - LockFileName = FileName; - LockFileName += ".lock"; - - // If the lock file already exists, don't bother to try to create our own - // lock file; it won't work anyway. Just figure out who owns this lock file. - if ((Owner = readLockFile(LockFileName))) - return; - - // Create a lock file that is unique to this instance. - UniqueLockFileName = LockFileName; - UniqueLockFileName += "-%%%%%%%%"; - int UniqueLockFileID; - if (llvm::error_code EC - = llvm::sys::fs::unique_file(UniqueLockFileName.str(), - UniqueLockFileID, - UniqueLockFileName, - /*makeAbsolute=*/false)) { - Error = EC; - return; - } - - // Write our process ID to our unique lock file. - { - llvm::raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true); - -#if LLVM_ON_UNIX - // FIXME: move getpid() call into LLVM - char hostname[256]; - hostname[255] = 0; - hostname[0] = 0; - gethostname(hostname, 255); - Out << hostname << ' ' << getpid(); -#else - Out << "localhost 1"; -#endif - Out.close(); - - if (Out.has_error()) { - // We failed to write out PID, so make up an excuse, remove the - // unique lock file, and fail. - Error = llvm::make_error_code(llvm::errc::no_space_on_device); - bool Existed; - llvm::sys::fs::remove(UniqueLockFileName.c_str(), Existed); - return; - } - } - - // Create a hard link from the lock file name. If this succeeds, we're done. - llvm::error_code EC - = llvm::sys::fs::create_hard_link(UniqueLockFileName.str(), - LockFileName.str()); - if (EC == llvm::errc::success) - return; - - // Creating the hard link failed. - -#ifdef LLVM_ON_UNIX - // The creation of the hard link may appear to fail, but if stat'ing the - // unique file returns a link count of 2, then we can still declare success. - struct stat StatBuf; - if (stat(UniqueLockFileName.c_str(), &StatBuf) == 0 && - StatBuf.st_nlink == 2) - return; -#endif - - // Someone else managed to create the lock file first. Wipe out our unique - // lock file (it's useless now) and read the process ID from the lock file. - bool Existed; - llvm::sys::fs::remove(UniqueLockFileName.str(), Existed); - if ((Owner = readLockFile(LockFileName))) - return; - - // There is a lock file that nobody owns; try to clean it up and report - // an error. - llvm::sys::fs::remove(LockFileName.str(), Existed); - Error = EC; -} - -LockFileManager::LockFileState LockFileManager::getState() const { - if (Owner) - return LFS_Shared; - - if (Error) - return LFS_Error; - - return LFS_Owned; -} - -LockFileManager::~LockFileManager() { - if (getState() != LFS_Owned) - return; - - // Since we own the lock, remove the lock file and our own unique lock file. - bool Existed; - llvm::sys::fs::remove(LockFileName.str(), Existed); - llvm::sys::fs::remove(UniqueLockFileName.str(), Existed); -} - -void LockFileManager::waitForUnlock() { - if (getState() != LFS_Shared) - return; - -#if LLVM_ON_WIN32 - unsigned long Interval = 1; -#else - struct timespec Interval; - Interval.tv_sec = 0; - Interval.tv_nsec = 1000000; -#endif - // Don't wait more than an hour for the file to appear. - const unsigned MaxSeconds = 3600; - do { - // Sleep for the designated interval, to allow the owning process time to - // finish up and - // FIXME: Should we hook in to system APIs to get a notification when the - // lock file is deleted? -#if LLVM_ON_WIN32 - Sleep(Interval); -#else - nanosleep(&Interval, NULL); -#endif - // If the file no longer exists, we're done. - bool Exists = false; - if (!llvm::sys::fs::exists(LockFileName.str(), Exists) && !Exists) - return; - - if (!processStillExecuting((*Owner).first, (*Owner).second)) - return; - - // Exponentially increase the time we wait for the lock to be removed. -#if LLVM_ON_WIN32 - Interval *= 2; -#else - Interval.tv_sec *= 2; - Interval.tv_nsec *= 2; - if (Interval.tv_nsec >= 1000000000) { - ++Interval.tv_sec; - Interval.tv_nsec -= 1000000000; - } -#endif - } while ( -#if LLVM_ON_WIN32 - Interval < MaxSeconds * 1000 -#else - Interval.tv_sec < (time_t)MaxSeconds -#endif - ); - - // Give up. -} - -/// \brief Compile a module file for the given module name with the given -/// umbrella header, using the options provided by the importing compiler -/// instance. +/// \brief Compile a module file for the given module, using the options +/// provided by the importing compiler instance. static void compileModule(CompilerInstance &ImportingInstance, - StringRef ModuleName, - StringRef ModuleFileName, - StringRef UmbrellaHeader) { - LockFileManager Locked(ModuleFileName); + Module *Module, + StringRef ModuleFileName) { + llvm::LockFileManager Locked(ModuleFileName); switch (Locked) { - case LockFileManager::LFS_Error: + case llvm::LockFileManager::LFS_Error: return; - case LockFileManager::LFS_Owned: + case llvm::LockFileManager::LFS_Owned: // We're responsible for building the module ourselves. Do so below. break; - case LockFileManager::LFS_Shared: + case llvm::LockFileManager::LFS_Shared: // Someone else is responsible for building the module. Wait for them to // finish. Locked.waitForUnlock(); break; } + ModuleMap &ModMap + = ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap(); + // Construct a compiler invocation for creating this module. - llvm::IntrusiveRefCntPtr<CompilerInvocation> Invocation + IntrusiveRefCntPtr<CompilerInvocation> Invocation (new CompilerInvocation(ImportingInstance.getInvocation())); + PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); + // For any options that aren't intended to affect how a module is built, // reset them to their default values. - Invocation->getLangOpts().resetNonModularOptions(); - Invocation->getPreprocessorOpts().resetNonModularOptions(); + Invocation->getLangOpts()->resetNonModularOptions(); + PPOpts.resetNonModularOptions(); + + // Note the name of the module we're building. + Invocation->getLangOpts()->CurrentModule = Module->getTopLevelModuleName(); // Note that this module is part of the module build path, so that we // can detect cycles in the module graph. - Invocation->getPreprocessorOpts().ModuleBuildPath.push_back(ModuleName); + PPOpts.ModuleBuildPath.push_back(Module->getTopLevelModuleName()); + // If there is a module map file, build the module using the module map. // Set up the inputs/outputs so that we build the module from its umbrella // header. FrontendOptions &FrontendOpts = Invocation->getFrontendOpts(); FrontendOpts.OutputFile = ModuleFileName.str(); FrontendOpts.DisableFree = false; FrontendOpts.Inputs.clear(); - FrontendOpts.Inputs.push_back( - std::make_pair(getSourceInputKindFromOptions(Invocation->getLangOpts()), - UmbrellaHeader)); + InputKind IK = getSourceInputKindFromOptions(*Invocation->getLangOpts()); + + // Get or create the module map that we'll use to build this module. + SmallString<128> TempModuleMapFileName; + if (const FileEntry *ModuleMapFile + = ModMap.getContainingModuleMapFile(Module)) { + // Use the module map where this module resides. + FrontendOpts.Inputs.push_back(FrontendInputFile(ModuleMapFile->getName(), + IK)); + } else { + // Create a temporary module map file. + TempModuleMapFileName = Module->Name; + TempModuleMapFileName += "-%%%%%%%%.map"; + int FD; + if (llvm::sys::fs::unique_file(TempModuleMapFileName.str(), FD, + TempModuleMapFileName, + /*makeAbsolute=*/true) + != llvm::errc::success) { + ImportingInstance.getDiagnostics().Report(diag::err_module_map_temp_file) + << TempModuleMapFileName; + return; + } + // Print the module map to this file. + llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true); + Module->print(OS); + FrontendOpts.Inputs.push_back( + FrontendInputFile(TempModuleMapFileName.str().str(), IK)); + } + // Don't free the remapped file buffers; they are owned by our caller. + PPOpts.RetainRemappedFileBuffers = true; + Invocation->getDiagnosticOpts().VerifyDiagnostics = 0; - - assert(ImportingInstance.getInvocation().getModuleHash() == - Invocation->getModuleHash() && "Module hash mismatch!"); - + Invocation->getModuleHash() && "Module hash mismatch!"); + // Construct a compiler instance that will be used to actually create the // module. CompilerInstance Instance; @@ -992,21 +821,39 @@ static void compileModule(CompilerInstance &ImportingInstance, &ImportingInstance.getDiagnosticClient(), /*ShouldOwnClient=*/true, /*ShouldCloneClient=*/true); - + // Construct a module-generating action. - GeneratePCHAction CreateModuleAction(true); - + GenerateModuleAction CreateModuleAction; + // Execute the action to actually build the module in-place. Use a separate // thread so that we get a stack large enough. const unsigned ThreadStackSize = 8 << 20; llvm::CrashRecoveryContext CRC; - CompileModuleData Data = { Instance, CreateModuleAction }; - CRC.RunSafelyOnThread(&doCompileModule, &Data, ThreadStackSize); + CompileModuleMapData Data = { Instance, CreateModuleAction }; + CRC.RunSafelyOnThread(&doCompileMapModule, &Data, ThreadStackSize); + + // Delete the temporary module map file. + // FIXME: Even though we're executing under crash protection, it would still + // be nice to do this with RemoveFileOnSignal when we can. However, that + // doesn't make sense for all clients, so clean this up manually. + if (!TempModuleMapFileName.empty()) + llvm::sys::Path(TempModuleMapFileName).eraseFromDisk(); } -ModuleKey CompilerInstance::loadModule(SourceLocation ImportLoc, - IdentifierInfo &ModuleName, - SourceLocation ModuleNameLoc) { +Module *CompilerInstance::loadModule(SourceLocation ImportLoc, + ModuleIdPath Path, + Module::NameVisibilityKind Visibility, + bool IsInclusionDirective) { + // If we've already handled this import, just return the cached result. + // This one-element cache is important to eliminate redundant diagnostics + // when both the preprocessor and parser see the same import declaration. + if (!ImportLoc.isInvalid() && LastModuleImportLoc == ImportLoc) { + // Make the named module visible. + if (LastModuleImportResult) + ModuleManager->makeModuleVisible(LastModuleImportResult, Visibility); + return LastModuleImportResult; + } + // Determine what file we're searching from. SourceManager &SourceMgr = getSourceManager(); SourceLocation ExpandedImportLoc = SourceMgr.getExpansionLoc(ImportLoc); @@ -1015,98 +862,236 @@ ModuleKey CompilerInstance::loadModule(SourceLocation ImportLoc, if (!CurFile) CurFile = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); - // Search for a module with the given name. - std::string UmbrellaHeader; - std::string ModuleFileName; - const FileEntry *ModuleFile - = PP->getHeaderSearchInfo().lookupModule(ModuleName.getName(), - &ModuleFileName, - &UmbrellaHeader); - - bool BuildingModule = false; - if (!ModuleFile && !UmbrellaHeader.empty()) { - // We didn't find the module, but there is an umbrella header that - // can be used to create the module file. Create a separate compilation - // module to do so. - - // Check whether there is a cycle in the module graph. - SmallVectorImpl<std::string> &ModuleBuildPath - = getPreprocessorOpts().ModuleBuildPath; - SmallVectorImpl<std::string>::iterator Pos - = std::find(ModuleBuildPath.begin(), ModuleBuildPath.end(), - ModuleName.getName()); - if (Pos != ModuleBuildPath.end()) { - llvm::SmallString<256> CyclePath; - for (; Pos != ModuleBuildPath.end(); ++Pos) { - CyclePath += *Pos; - CyclePath += " -> "; - } - CyclePath += ModuleName.getName(); - - getDiagnostics().Report(ModuleNameLoc, diag::err_module_cycle) - << ModuleName.getName() << CyclePath; + StringRef ModuleName = Path[0].first->getName(); + SourceLocation ModuleNameLoc = Path[0].second; + + clang::Module *Module = 0; + + // If we don't already have information on this module, load the module now. + llvm::DenseMap<const IdentifierInfo *, clang::Module *>::iterator Known + = KnownModules.find(Path[0].first); + if (Known != KnownModules.end()) { + // Retrieve the cached top-level module. + Module = Known->second; + } else if (ModuleName == getLangOpts().CurrentModule) { + // This is the module we're building. + Module = PP->getHeaderSearchInfo().getModuleMap().findModule(ModuleName); + Known = KnownModules.insert(std::make_pair(Path[0].first, Module)).first; + } else { + // Search for a module with the given name. + Module = PP->getHeaderSearchInfo().lookupModule(ModuleName); + std::string ModuleFileName; + if (Module) + ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName(Module); + else + ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName(ModuleName); + + if (ModuleFileName.empty()) { + getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found) + << ModuleName + << SourceRange(ImportLoc, ModuleNameLoc); + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = 0; return 0; } + + const FileEntry *ModuleFile + = getFileManager().getFile(ModuleFileName, /*OpenFile=*/false, + /*CacheFailure=*/false); + bool BuildingModule = false; + if (!ModuleFile && Module) { + // The module is not cached, but we have a module map from which we can + // build the module. + + // Check whether there is a cycle in the module graph. + SmallVectorImpl<std::string> &ModuleBuildPath + = getPreprocessorOpts().ModuleBuildPath; + SmallVectorImpl<std::string>::iterator Pos + = std::find(ModuleBuildPath.begin(), ModuleBuildPath.end(), ModuleName); + if (Pos != ModuleBuildPath.end()) { + SmallString<256> CyclePath; + for (; Pos != ModuleBuildPath.end(); ++Pos) { + CyclePath += *Pos; + CyclePath += " -> "; + } + CyclePath += ModuleName; - getDiagnostics().Report(ModuleNameLoc, diag::warn_module_build) - << ModuleName.getName(); - BuildingModule = true; - compileModule(*this, ModuleName.getName(), ModuleFileName, UmbrellaHeader); - ModuleFile = PP->getHeaderSearchInfo().lookupModule(ModuleName.getName()); - } + getDiagnostics().Report(ModuleNameLoc, diag::err_module_cycle) + << ModuleName << CyclePath; + return 0; + } - if (!ModuleFile) { - getDiagnostics().Report(ModuleNameLoc, - BuildingModule? diag::err_module_not_built - : diag::err_module_not_found) - << ModuleName.getName() - << SourceRange(ImportLoc, ModuleNameLoc); - return 0; - } + getDiagnostics().Report(ModuleNameLoc, diag::warn_module_build) + << ModuleName; + BuildingModule = true; + compileModule(*this, Module, ModuleFileName); + ModuleFile = FileMgr->getFile(ModuleFileName); + } - // If we don't already have an ASTReader, create one now. - if (!ModuleManager) { - if (!hasASTContext()) - createASTContext(); - - std::string Sysroot = getHeaderSearchOpts().Sysroot; - const PreprocessorOptions &PPOpts = getPreprocessorOpts(); - ModuleManager = new ASTReader(getPreprocessor(), *Context, - Sysroot.empty() ? "" : Sysroot.c_str(), - PPOpts.DisablePCHValidation, - PPOpts.DisableStatCache); - if (hasASTConsumer()) { - ModuleManager->setDeserializationListener( - getASTConsumer().GetASTDeserializationListener()); - getASTContext().setASTMutationListener( - getASTConsumer().GetASTMutationListener()); + if (!ModuleFile) { + getDiagnostics().Report(ModuleNameLoc, + BuildingModule? diag::err_module_not_built + : diag::err_module_not_found) + << ModuleName + << SourceRange(ImportLoc, ModuleNameLoc); + return 0; } - llvm::OwningPtr<ExternalASTSource> Source; - Source.reset(ModuleManager); - getASTContext().setExternalSource(Source); - if (hasSema()) - ModuleManager->InitializeSema(getSema()); - if (hasASTConsumer()) - ModuleManager->StartTranslationUnit(&getASTConsumer()); - } - // Try to load the module we found. - switch (ModuleManager->ReadAST(ModuleFile->getName(), - serialization::MK_Module)) { - case ASTReader::Success: - break; + // If we don't already have an ASTReader, create one now. + if (!ModuleManager) { + if (!hasASTContext()) + createASTContext(); + + std::string Sysroot = getHeaderSearchOpts().Sysroot; + const PreprocessorOptions &PPOpts = getPreprocessorOpts(); + ModuleManager = new ASTReader(getPreprocessor(), *Context, + Sysroot.empty() ? "" : Sysroot.c_str(), + PPOpts.DisablePCHValidation, + PPOpts.DisableStatCache); + if (hasASTConsumer()) { + ModuleManager->setDeserializationListener( + getASTConsumer().GetASTDeserializationListener()); + getASTContext().setASTMutationListener( + getASTConsumer().GetASTMutationListener()); + } + OwningPtr<ExternalASTSource> Source; + Source.reset(ModuleManager); + getASTContext().setExternalSource(Source); + if (hasSema()) + ModuleManager->InitializeSema(getSema()); + if (hasASTConsumer()) + ModuleManager->StartTranslationUnit(&getASTConsumer()); + } - case ASTReader::IgnorePCH: - // FIXME: The ASTReader will already have complained, but can we showhorn - // that diagnostic information into a more useful form? - return 0; + // Try to load the module we found. + switch (ModuleManager->ReadAST(ModuleFile->getName(), + serialization::MK_Module)) { + case ASTReader::Success: + break; - case ASTReader::Failure: - // Already complained. + case ASTReader::IgnorePCH: + // FIXME: The ASTReader will already have complained, but can we showhorn + // that diagnostic information into a more useful form? + KnownModules[Path[0].first] = 0; + return 0; + + case ASTReader::Failure: + // Already complained, but note now that we failed. + KnownModules[Path[0].first] = 0; + return 0; + } + + if (!Module) { + // If we loaded the module directly, without finding a module map first, + // we'll have loaded the module's information from the module itself. + Module = PP->getHeaderSearchInfo().getModuleMap() + .findModule((Path[0].first->getName())); + } + + // Cache the result of this top-level module lookup for later. + Known = KnownModules.insert(std::make_pair(Path[0].first, Module)).first; + } + + // If we never found the module, fail. + if (!Module) return 0; + + // Verify that the rest of the module path actually corresponds to + // a submodule. + if (Path.size() > 1) { + for (unsigned I = 1, N = Path.size(); I != N; ++I) { + StringRef Name = Path[I].first->getName(); + clang::Module *Sub = Module->findSubmodule(Name); + + if (!Sub) { + // Attempt to perform typo correction to find a module name that works. + llvm::SmallVector<StringRef, 2> Best; + unsigned BestEditDistance = (std::numeric_limits<unsigned>::max)(); + + for (clang::Module::submodule_iterator J = Module->submodule_begin(), + JEnd = Module->submodule_end(); + J != JEnd; ++J) { + unsigned ED = Name.edit_distance((*J)->Name, + /*AllowReplacements=*/true, + BestEditDistance); + if (ED <= BestEditDistance) { + if (ED < BestEditDistance) { + Best.clear(); + BestEditDistance = ED; + } + + Best.push_back((*J)->Name); + } + } + + // If there was a clear winner, user it. + if (Best.size() == 1) { + getDiagnostics().Report(Path[I].second, + diag::err_no_submodule_suggest) + << Path[I].first << Module->getFullModuleName() << Best[0] + << SourceRange(Path[0].second, Path[I-1].second) + << FixItHint::CreateReplacement(SourceRange(Path[I].second), + Best[0]); + + Sub = Module->findSubmodule(Best[0]); + } + } + + if (!Sub) { + // No submodule by this name. Complain, and don't look for further + // submodules. + getDiagnostics().Report(Path[I].second, diag::err_no_submodule) + << Path[I].first << Module->getFullModuleName() + << SourceRange(Path[0].second, Path[I-1].second); + break; + } + + Module = Sub; + } } + + // Make the named module visible, if it's not already part of the module + // we are parsing. + if (ModuleName != getLangOpts().CurrentModule) { + if (!Module->IsFromModuleFile) { + // We have an umbrella header or directory that doesn't actually include + // all of the headers within the directory it covers. Complain about + // this missing submodule and recover by forgetting that we ever saw + // this submodule. + // FIXME: Should we detect this at module load time? It seems fairly + // expensive (and rare). + getDiagnostics().Report(ImportLoc, diag::warn_missing_submodule) + << Module->getFullModuleName() + << SourceRange(Path.front().second, Path.back().second); + + return 0; + } - // FIXME: The module file's FileEntry makes a poor key indeed! - return (ModuleKey)ModuleFile; -} + // Check whether this module is available. + StringRef Feature; + if (!Module->isAvailable(getLangOpts(), getTarget(), Feature)) { + getDiagnostics().Report(ImportLoc, diag::err_module_unavailable) + << Module->getFullModuleName() + << Feature + << SourceRange(Path.front().second, Path.back().second); + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = 0; + return 0; + } + ModuleManager->makeModuleVisible(Module, Visibility); + } + + // If this module import was due to an inclusion directive, create an + // implicit import declaration to capture it in the AST. + if (IsInclusionDirective && hasASTContext()) { + TranslationUnitDecl *TU = getASTContext().getTranslationUnitDecl(); + TU->addDecl(ImportDecl::CreateImplicit(getASTContext(), TU, + ImportLoc, Module, + Path.back().second)); + } + + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = Module; + return Module; +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp b/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp index e926b89..612a0d8 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp @@ -30,6 +30,21 @@ #include "llvm/Support/Path.h" using namespace clang; +//===----------------------------------------------------------------------===// +// Initialization. +//===----------------------------------------------------------------------===// + +CompilerInvocationBase::CompilerInvocationBase() + : LangOpts(new LangOptions()) {} + +CompilerInvocationBase::CompilerInvocationBase(const CompilerInvocationBase &X) + : RefCountedBase<CompilerInvocation>(), + LangOpts(new LangOptions(*X.getLangOpts())) {} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + static const char *getAnalysisStoreName(AnalysisStores Kind) { switch (Kind) { default: @@ -63,41 +78,81 @@ static const char *getAnalysisDiagClientName(AnalysisDiagClients Kind) { static const char *getAnalysisPurgeModeName(AnalysisPurgeMode Kind) { switch (Kind) { default: - llvm_unreachable("Unknown analysis client!"); + llvm_unreachable("Unknown analysis purge mode!"); #define ANALYSIS_PURGE(NAME, CMDFLAG, DESC) \ case NAME: return CMDFLAG; #include "clang/Frontend/Analyses.def" } } +static const char *getAnalysisIPAModeName(AnalysisIPAMode Kind) { + switch (Kind) { + default: + llvm_unreachable("Unknown analysis ipa mode!"); +#define ANALYSIS_IPA(NAME, CMDFLAG, DESC) \ + case NAME: return CMDFLAG; +#include "clang/Frontend/Analyses.def" + } +} + +static const char * + getAnalysisInliningModeName(AnalysisInliningMode Kind) { + switch (Kind) { + default: + llvm_unreachable("Unknown analysis inlining mode!"); +#define ANALYSIS_INLINE_SELECTION(NAME, CMDFLAG, DESC) \ + case NAME: return CMDFLAG; +#include "clang/Frontend/Analyses.def" + } +} + //===----------------------------------------------------------------------===// // Serialization (to args) //===----------------------------------------------------------------------===// -static void AnalyzerOptsToArgs(const AnalyzerOptions &Opts, - std::vector<std::string> &Res) { +namespace { + /// ToArgsList - Helper class to create a list of std::strings. + class ToArgsList { + std::vector<std::string> &Res; + public: + explicit ToArgsList(std::vector<std::string> &Res) : Res(Res) {} + + void push_back(StringRef Str) { + // Avoid creating a temporary string. + Res.push_back(std::string()); + Res.back().assign(Str.data(), Str.size()); + } + + void push_back(StringRef Str1, StringRef Str2) { + push_back(Str1); + push_back(Str2); + } + }; +} + +static void AnalyzerOptsToArgs(const AnalyzerOptions &Opts, ToArgsList &Res) { if (Opts.ShowCheckerHelp) Res.push_back("-analyzer-checker-help"); - if (Opts.AnalysisStoreOpt != RegionStoreModel) { - Res.push_back("-analyzer-store"); - Res.push_back(getAnalysisStoreName(Opts.AnalysisStoreOpt)); - } - if (Opts.AnalysisConstraintsOpt != RangeConstraintsModel) { - Res.push_back("-analyzer-constraints"); - Res.push_back(getAnalysisConstraintName(Opts.AnalysisConstraintsOpt)); - } - if (Opts.AnalysisDiagOpt != PD_HTML) { - Res.push_back("-analyzer-output"); - Res.push_back(getAnalysisDiagClientName(Opts.AnalysisDiagOpt)); - } - if (Opts.AnalysisPurgeOpt != PurgeStmt) { - Res.push_back("-analyzer-purge"); - Res.push_back(getAnalysisPurgeModeName(Opts.AnalysisPurgeOpt)); - } - if (!Opts.AnalyzeSpecificFunction.empty()) { - Res.push_back("-analyze-function"); - Res.push_back(Opts.AnalyzeSpecificFunction); - } + if (Opts.AnalysisStoreOpt != RegionStoreModel) + Res.push_back("-analyzer-store", + getAnalysisStoreName(Opts.AnalysisStoreOpt)); + if (Opts.AnalysisConstraintsOpt != RangeConstraintsModel) + Res.push_back("-analyzer-constraints", + getAnalysisConstraintName(Opts.AnalysisConstraintsOpt)); + if (Opts.AnalysisDiagOpt != PD_HTML) + Res.push_back("-analyzer-output", + getAnalysisDiagClientName(Opts.AnalysisDiagOpt)); + if (Opts.AnalysisPurgeOpt != PurgeStmt) + Res.push_back("-analyzer-purge", + getAnalysisPurgeModeName(Opts.AnalysisPurgeOpt)); + if (!Opts.AnalyzeSpecificFunction.empty()) + Res.push_back("-analyze-function", Opts.AnalyzeSpecificFunction); + if (Opts.IPAMode != Inlining) + Res.push_back("-analyzer-ipa", getAnalysisIPAModeName(Opts.IPAMode)); + if (Opts.InliningMode != NoRedundancy) + Res.push_back("-analyzer-inlining-mode", + getAnalysisInliningModeName(Opts.InliningMode)); + if (Opts.AnalyzeAll) Res.push_back("-analyzer-opt-analyze-headers"); if (Opts.AnalyzerDisplayProgress) @@ -112,6 +167,8 @@ static void AnalyzerOptsToArgs(const AnalyzerOptions &Opts, Res.push_back("-analyzer-viz-egraph-graphviz"); if (Opts.VisualizeEGUbi) Res.push_back("-analyzer-viz-egraph-ubigraph"); + if (Opts.NoRetryExhausted) + Res.push_back("-analyzer-disable-retry-exhausted"); for (unsigned i = 0, e = Opts.CheckersControlList.size(); i != e; ++i) { const std::pair<std::string, bool> &opt = Opts.CheckersControlList[i]; @@ -123,18 +180,19 @@ static void AnalyzerOptsToArgs(const AnalyzerOptions &Opts, } } -static void CodeGenOptsToArgs(const CodeGenOptions &Opts, - std::vector<std::string> &Res) { +static void CodeGenOptsToArgs(const CodeGenOptions &Opts, ToArgsList &Res) { if (Opts.DebugInfo) Res.push_back("-g"); if (Opts.DisableLLVMOpts) Res.push_back("-disable-llvm-optzns"); if (Opts.DisableRedZone) Res.push_back("-disable-red-zone"); - if (!Opts.DwarfDebugFlags.empty()) { - Res.push_back("-dwarf-debug-flags"); - Res.push_back(Opts.DwarfDebugFlags); - } + if (Opts.DisableTailCalls) + Res.push_back("-mdisable-tail-calls"); + if (!Opts.DebugCompilationDir.empty()) + Res.push_back("-fdebug-compilation-dir", Opts.DebugCompilationDir); + if (!Opts.DwarfDebugFlags.empty()) + Res.push_back("-dwarf-debug-flags", Opts.DwarfDebugFlags); if (Opts.ObjCRuntimeHasARC) Res.push_back("-fobjc-runtime-has-arc"); if (Opts.ObjCRuntimeHasTerminate) @@ -160,10 +218,12 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, Opts.OptimizeSize == 1 ? Res.push_back("-Os") : Res.push_back("-Oz"); } else if (Opts.OptimizationLevel != 0) Res.push_back("-O" + llvm::utostr(Opts.OptimizationLevel)); - if (!Opts.MainFileName.empty()) { - Res.push_back("-main-file-name"); - Res.push_back(Opts.MainFileName); - } + if (!Opts.MainFileName.empty()) + Res.push_back("-main-file-name", Opts.MainFileName); + if (Opts.NoInfsFPMath) + Res.push_back("-menable-no-infinities"); + if (Opts.NoNaNsFPMath) + Res.push_back("-menable-no-nans"); // SimplifyLibCalls is only derived. // TimePasses is only derived. // UnitAtATime is unused. @@ -179,10 +239,8 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, Res.push_back("-ffunction-sections"); if (Opts.AsmVerbose) Res.push_back("-masm-verbose"); - if (!Opts.CodeModel.empty()) { - Res.push_back("-mcode-model"); - Res.push_back(Opts.CodeModel); - } + if (!Opts.CodeModel.empty()) + Res.push_back("-mcode-model", Opts.CodeModel); if (Opts.CUDAIsDevice) Res.push_back("-fcuda-is-device"); if (!Opts.CXAAtExit) @@ -192,19 +250,14 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, if (Opts.ObjCAutoRefCountExceptions) Res.push_back("-fobjc-arc-eh"); if (!Opts.DebugPass.empty()) { - Res.push_back("-mdebug-pass"); - Res.push_back(Opts.DebugPass); + Res.push_back("-mdebug-pass", Opts.DebugPass); } if (Opts.DisableFPElim) Res.push_back("-mdisable-fp-elim"); - if (!Opts.FloatABI.empty()) { - Res.push_back("-mfloat-abi"); - Res.push_back(Opts.FloatABI); - } - if (!Opts.LimitFloatPrecision.empty()) { - Res.push_back("-mlimit-float-precision"); - Res.push_back(Opts.LimitFloatPrecision); - } + if (!Opts.FloatABI.empty()) + Res.push_back("-mfloat-abi", Opts.FloatABI); + if (!Opts.LimitFloatPrecision.empty()) + Res.push_back("-mlimit-float-precision", Opts.LimitFloatPrecision); if (Opts.NoZeroInitializedInBSS) Res.push_back("-mno-zero-initialized-bss"); switch (Opts.getObjCDispatchMethod()) { @@ -217,10 +270,8 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, Res.push_back("-fobjc-dispatch-method=non-legacy"); break; } - if (Opts.NumRegisterParameters) { - Res.push_back("-mregparm"); - Res.push_back(llvm::utostr(Opts.NumRegisterParameters)); - } + if (Opts.NumRegisterParameters) + Res.push_back("-mregparm", llvm::utostr(Opts.NumRegisterParameters)); if (Opts.NoGlobalMerge) Res.push_back("-mno-global-merge"); if (Opts.NoExecStack) @@ -231,46 +282,40 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, Res.push_back("-msave-temp-labels"); if (Opts.NoDwarf2CFIAsm) Res.push_back("-fno-dwarf2-cfi-asm"); + if (Opts.NoDwarfDirectoryAsm) + Res.push_back("-fno-dwarf-directory-asm"); if (Opts.SoftFloat) Res.push_back("-msoft-float"); + if (Opts.StrictEnums) + Res.push_back("-fstrict-enums"); if (Opts.UnwindTables) Res.push_back("-munwind-tables"); - if (Opts.RelocationModel != "pic") { - Res.push_back("-mrelocation-model"); - Res.push_back(Opts.RelocationModel); - } + if (Opts.RelocationModel != "pic") + Res.push_back("-mrelocation-model", Opts.RelocationModel); if (!Opts.VerifyModule) Res.push_back("-disable-llvm-verifier"); - for (unsigned i = 0, e = Opts.BackendOptions.size(); i != e; ++i) { - Res.push_back("-backend-option"); - Res.push_back(Opts.BackendOptions[i]); - } + for (unsigned i = 0, e = Opts.BackendOptions.size(); i != e; ++i) + Res.push_back("-backend-option", Opts.BackendOptions[i]); } static void DependencyOutputOptsToArgs(const DependencyOutputOptions &Opts, - std::vector<std::string> &Res) { + ToArgsList &Res) { if (Opts.IncludeSystemHeaders) Res.push_back("-sys-header-deps"); if (Opts.ShowHeaderIncludes) Res.push_back("-H"); - if (!Opts.HeaderIncludeOutputFile.empty()) { - Res.push_back("-header-include-file"); - Res.push_back(Opts.HeaderIncludeOutputFile); - } + if (!Opts.HeaderIncludeOutputFile.empty()) + Res.push_back("-header-include-file", Opts.HeaderIncludeOutputFile); if (Opts.UsePhonyTargets) Res.push_back("-MP"); - if (!Opts.OutputFile.empty()) { - Res.push_back("-dependency-file"); - Res.push_back(Opts.OutputFile); - } - for (unsigned i = 0, e = Opts.Targets.size(); i != e; ++i) { - Res.push_back("-MT"); - Res.push_back(Opts.Targets[i]); - } + if (!Opts.OutputFile.empty()) + Res.push_back("-dependency-file", Opts.OutputFile); + for (unsigned i = 0, e = Opts.Targets.size(); i != e; ++i) + Res.push_back("-MT", Opts.Targets[i]); } static void DiagnosticOptsToArgs(const DiagnosticOptions &Opts, - std::vector<std::string> &Res) { + ToArgsList &Res) { if (Opts.IgnoreWarnings) Res.push_back("-w"); if (Opts.NoRewriteMacros) @@ -295,8 +340,6 @@ static void DiagnosticOptsToArgs(const DiagnosticOptions &Opts, Res.push_back("-fcolor-diagnostics"); if (Opts.VerifyDiagnostics) Res.push_back("-verify"); - if (Opts.ShowNames) - Res.push_back("-fdiagnostics-show-name"); if (Opts.ShowOptionNames) Res.push_back("-fdiagnostics-show-option"); if (Opts.ShowCategories == 1) @@ -311,37 +354,29 @@ static void DiagnosticOptsToArgs(const DiagnosticOptions &Opts, case DiagnosticOptions::Vi: Res.push_back("-fdiagnostics-format=vi"); break; } - if (Opts.ErrorLimit) { - Res.push_back("-ferror-limit"); - Res.push_back(llvm::utostr(Opts.ErrorLimit)); - } - if (!Opts.DiagnosticLogFile.empty()) { - Res.push_back("-diagnostic-log-file"); - Res.push_back(Opts.DiagnosticLogFile); - } + if (Opts.ErrorLimit) + Res.push_back("-ferror-limit", llvm::utostr(Opts.ErrorLimit)); + if (!Opts.DiagnosticLogFile.empty()) + Res.push_back("-diagnostic-log-file", Opts.DiagnosticLogFile); if (Opts.MacroBacktraceLimit - != DiagnosticOptions::DefaultMacroBacktraceLimit) { - Res.push_back("-fmacro-backtrace-limit"); - Res.push_back(llvm::utostr(Opts.MacroBacktraceLimit)); - } + != DiagnosticOptions::DefaultMacroBacktraceLimit) + Res.push_back("-fmacro-backtrace-limit", + llvm::utostr(Opts.MacroBacktraceLimit)); if (Opts.TemplateBacktraceLimit - != DiagnosticOptions::DefaultTemplateBacktraceLimit) { - Res.push_back("-ftemplate-backtrace-limit"); - Res.push_back(llvm::utostr(Opts.TemplateBacktraceLimit)); - } - - if (Opts.TabStop != DiagnosticOptions::DefaultTabStop) { - Res.push_back("-ftabstop"); - Res.push_back(llvm::utostr(Opts.TabStop)); - } - if (Opts.MessageLength) { - Res.push_back("-fmessage-length"); - Res.push_back(llvm::utostr(Opts.MessageLength)); - } - if (!Opts.DumpBuildInformation.empty()) { - Res.push_back("-dump-build-information"); - Res.push_back(Opts.DumpBuildInformation); - } + != DiagnosticOptions::DefaultTemplateBacktraceLimit) + Res.push_back("-ftemplate-backtrace-limit", + llvm::utostr(Opts.TemplateBacktraceLimit)); + if (Opts.ConstexprBacktraceLimit + != DiagnosticOptions::DefaultConstexprBacktraceLimit) + Res.push_back("-fconstexpr-backtrace-limit", + llvm::utostr(Opts.ConstexprBacktraceLimit)); + + if (Opts.TabStop != DiagnosticOptions::DefaultTabStop) + Res.push_back("-ftabstop", llvm::utostr(Opts.TabStop)); + if (Opts.MessageLength) + Res.push_back("-fmessage-length", llvm::utostr(Opts.MessageLength)); + if (!Opts.DumpBuildInformation.empty()) + Res.push_back("-dump-build-information", Opts.DumpBuildInformation); for (unsigned i = 0, e = Opts.Warnings.size(); i != e; ++i) Res.push_back("-W" + Opts.Warnings[i]); } @@ -365,7 +400,6 @@ static const char *getInputKindName(InputKind Kind) { } llvm_unreachable("Unexpected language kind!"); - return 0; } static const char *getActionName(frontend::ActionKind Kind) { @@ -395,27 +429,24 @@ static const char *getActionName(frontend::ActionKind Kind) { case frontend::PrintDeclContext: return "-print-decl-contexts"; case frontend::PrintPreamble: return "-print-preamble"; case frontend::PrintPreprocessedInput: return "-E"; + case frontend::PubnamesDump: return "-pubnames-dump"; case frontend::RewriteMacros: return "-rewrite-macros"; case frontend::RewriteObjC: return "-rewrite-objc"; case frontend::RewriteTest: return "-rewrite-test"; case frontend::RunAnalysis: return "-analyze"; + case frontend::MigrateSource: return "-migrate"; case frontend::RunPreprocessorOnly: return "-Eonly"; } llvm_unreachable("Unexpected language kind!"); - return 0; } -static void FileSystemOptsToArgs(const FileSystemOptions &Opts, - std::vector<std::string> &Res) { - if (!Opts.WorkingDir.empty()) { - Res.push_back("-working-directory"); - Res.push_back(Opts.WorkingDir); - } +static void FileSystemOptsToArgs(const FileSystemOptions &Opts, ToArgsList &Res){ + if (!Opts.WorkingDir.empty()) + Res.push_back("-working-directory", Opts.WorkingDir); } -static void FrontendOptsToArgs(const FrontendOptions &Opts, - std::vector<std::string> &Res) { +static void FrontendOptsToArgs(const FrontendOptions &Opts, ToArgsList &Res) { if (Opts.DisableFree) Res.push_back("-disable-free"); if (Opts.RelocatablePCH) @@ -436,6 +467,12 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, Res.push_back("-version"); if (Opts.FixWhatYouCan) Res.push_back("-fix-what-you-can"); + if (Opts.FixOnlyWarnings) + Res.push_back("-fix-only-warnings"); + if (Opts.FixAndRecompile) + Res.push_back("-fixit-recompile"); + if (Opts.FixToTemporaries) + Res.push_back("-fixit-to-temporary"); switch (Opts.ARCMTAction) { case FrontendOptions::ARCMT_None: break; @@ -449,76 +486,63 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, Res.push_back("-arcmt-migrate"); break; } - if (!Opts.ARCMTMigrateDir.empty()) { - Res.push_back("-arcmt-migrate-directory"); - Res.push_back(Opts.ARCMTMigrateDir); - } - if (!Opts.ARCMTMigrateReportOut.empty()) { - Res.push_back("-arcmt-migrate-report-output"); - Res.push_back(Opts.ARCMTMigrateReportOut); - } + if (!Opts.MTMigrateDir.empty()) + Res.push_back("-mt-migrate-directory", Opts.MTMigrateDir); + if (!Opts.ARCMTMigrateReportOut.empty()) + Res.push_back("-arcmt-migrate-report-output", Opts.ARCMTMigrateReportOut); if (Opts.ARCMTMigrateEmitARCErrors) Res.push_back("-arcmt-migrate-emit-errors"); + if (Opts.ObjCMTAction & ~FrontendOptions::ObjCMT_Literals) + Res.push_back("-objcmt-migrate-literals"); + if (Opts.ObjCMTAction & ~FrontendOptions::ObjCMT_Subscripting) + Res.push_back("-objcmt-migrate-subscripting"); + bool NeedLang = false; for (unsigned i = 0, e = Opts.Inputs.size(); i != e; ++i) - if (FrontendOptions::getInputKindForExtension(Opts.Inputs[i].second) != - Opts.Inputs[i].first) + if (FrontendOptions::getInputKindForExtension(Opts.Inputs[i].File) != + Opts.Inputs[i].Kind) NeedLang = true; - if (NeedLang) { - Res.push_back("-x"); - Res.push_back(getInputKindName(Opts.Inputs[0].first)); - } + if (NeedLang) + Res.push_back("-x", getInputKindName(Opts.Inputs[0].Kind)); for (unsigned i = 0, e = Opts.Inputs.size(); i != e; ++i) { - assert((!NeedLang || Opts.Inputs[i].first == Opts.Inputs[0].first) && + assert((!NeedLang || Opts.Inputs[i].Kind == Opts.Inputs[0].Kind) && "Unable to represent this input vector!"); - Res.push_back(Opts.Inputs[i].second); + Res.push_back(Opts.Inputs[i].File); } - if (!Opts.OutputFile.empty()) { - Res.push_back("-o"); - Res.push_back(Opts.OutputFile); - } - if (!Opts.CodeCompletionAt.FileName.empty()) { - Res.push_back("-code-completion-at"); - Res.push_back(Opts.CodeCompletionAt.FileName + ":" + + if (!Opts.OutputFile.empty()) + Res.push_back("-o", Opts.OutputFile); + if (!Opts.CodeCompletionAt.FileName.empty()) + Res.push_back("-code-completion-at", + Opts.CodeCompletionAt.FileName + ":" + llvm::utostr(Opts.CodeCompletionAt.Line) + ":" + llvm::utostr(Opts.CodeCompletionAt.Column)); - } if (Opts.ProgramAction != frontend::PluginAction) Res.push_back(getActionName(Opts.ProgramAction)); if (!Opts.ActionName.empty()) { - Res.push_back("-plugin"); - Res.push_back(Opts.ActionName); - for(unsigned i = 0, e = Opts.PluginArgs.size(); i != e; ++i) { - Res.push_back("-plugin-arg-" + Opts.ActionName); - Res.push_back(Opts.PluginArgs[i]); - } - } - for (unsigned i = 0, e = Opts.Plugins.size(); i != e; ++i) { - Res.push_back("-load"); - Res.push_back(Opts.Plugins[i]); + Res.push_back("-plugin", Opts.ActionName); + for(unsigned i = 0, e = Opts.PluginArgs.size(); i != e; ++i) + Res.push_back("-plugin-arg-" + Opts.ActionName, Opts.PluginArgs[i]); } + for (unsigned i = 0, e = Opts.Plugins.size(); i != e; ++i) + Res.push_back("-load", Opts.Plugins[i]); for (unsigned i = 0, e = Opts.AddPluginActions.size(); i != e; ++i) { - Res.push_back("-add-plugin"); - Res.push_back(Opts.AddPluginActions[i]); - for(unsigned ai = 0, ae = Opts.AddPluginArgs.size(); ai != ae; ++ai) { - Res.push_back("-plugin-arg-" + Opts.AddPluginActions[i]); - Res.push_back(Opts.AddPluginArgs[i][ai]); - } - } - for (unsigned i = 0, e = Opts.ASTMergeFiles.size(); i != e; ++i) { - Res.push_back("-ast-merge"); - Res.push_back(Opts.ASTMergeFiles[i]); - } - for (unsigned i = 0, e = Opts.LLVMArgs.size(); i != e; ++i) { - Res.push_back("-mllvm"); - Res.push_back(Opts.LLVMArgs[i]); - } + Res.push_back("-add-plugin", Opts.AddPluginActions[i]); + for(unsigned ai = 0, ae = Opts.AddPluginArgs.size(); ai != ae; ++ai) + Res.push_back("-plugin-arg-" + Opts.AddPluginActions[i], + Opts.AddPluginArgs[i][ai]); + } + for (unsigned i = 0, e = Opts.ASTMergeFiles.size(); i != e; ++i) + Res.push_back("-ast-merge", Opts.ASTMergeFiles[i]); + for (unsigned i = 0, e = Opts.LLVMArgs.size(); i != e; ++i) + Res.push_back("-mllvm", Opts.LLVMArgs[i]); + if (!Opts.OverrideRecordLayoutsFile.empty()) + Res.push_back("-foverride-record-layout=" + Opts.OverrideRecordLayoutsFile); } static void HeaderSearchOptsToArgs(const HeaderSearchOptions &Opts, - std::vector<std::string> &Res) { + ToArgsList &Res) { if (Opts.Sysroot != "/") { Res.push_back("-isysroot"); Res.push_back(Opts.Sysroot); @@ -585,14 +609,10 @@ static void HeaderSearchOptsToArgs(const HeaderSearchOptions &Opts, Res.push_back(E.Path); } - if (!Opts.ResourceDir.empty()) { - Res.push_back("-resource-dir"); - Res.push_back(Opts.ResourceDir); - } - if (!Opts.ModuleCachePath.empty()) { - Res.push_back("-fmodule-cache-path"); - Res.push_back(Opts.ModuleCachePath); - } + if (!Opts.ResourceDir.empty()) + Res.push_back("-resource-dir", Opts.ResourceDir); + if (!Opts.ModuleCachePath.empty()) + Res.push_back("-fmodule-cache-path", Opts.ModuleCachePath); if (!Opts.UseStandardSystemIncludes) Res.push_back("-nostdsysteminc"); if (!Opts.UseStandardCXXIncludes) @@ -603,8 +623,7 @@ static void HeaderSearchOptsToArgs(const HeaderSearchOptions &Opts, Res.push_back("-v"); } -static void LangOptsToArgs(const LangOptions &Opts, - std::vector<std::string> &Res) { +static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) { LangOptions DefaultLangOpts; // FIXME: Need to set -std to get all the implicit options. @@ -629,6 +648,8 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-fgnu-keywords"); if (Opts.MicrosoftExt) Res.push_back("-fms-extensions"); + if (Opts.MicrosoftMode) + Res.push_back("-fms-compatibility"); if (Opts.MSCVersion != 0) Res.push_back("-fmsc-version=" + llvm::utostr(Opts.MSCVersion)); if (Opts.Borland) @@ -644,6 +665,10 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-fpascal-strings"); if (Opts.CatchUndefined) Res.push_back("-fcatch-undefined-behavior"); + if (Opts.AddressSanitizer) + Res.push_back("-faddress-sanitizer"); + if (Opts.ThreadSanitizer) + Res.push_back("-fthread-sanitizer"); if (Opts.WritableStrings) Res.push_back("-fwritable-strings"); if (Opts.ConstStrings) @@ -684,6 +709,8 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-fblocks"); if (Opts.BlocksRuntimeOptional) Res.push_back("-fblocks-runtime-optional"); + if (Opts.Modules) + Res.push_back("-fmodules"); if (Opts.EmitAllDecls) Res.push_back("-femit-all-decls"); if (Opts.MathErrno) @@ -692,28 +719,31 @@ static void LangOptsToArgs(const LangOptions &Opts, case LangOptions::SOB_Undefined: break; case LangOptions::SOB_Defined: Res.push_back("-fwrapv"); break; case LangOptions::SOB_Trapping: - Res.push_back("-ftrapv"); break; - if (!Opts.OverflowHandler.empty()) { - Res.push_back("-ftrapv-handler"); - Res.push_back(Opts.OverflowHandler); - } + Res.push_back("-ftrapv"); + if (!Opts.OverflowHandler.empty()) + Res.push_back("-ftrapv-handler", Opts.OverflowHandler); + break; } if (Opts.HeinousExtensions) Res.push_back("-fheinous-gnu-extensions"); // Optimize is implicit. // OptimizeSize is implicit. + if (Opts.FastMath) + Res.push_back("-ffast-math"); if (Opts.Static) Res.push_back("-static-define"); - if (Opts.DumpRecordLayouts) + if (Opts.DumpRecordLayoutsSimple) + Res.push_back("-fdump-record-layouts-simple"); + else if (Opts.DumpRecordLayouts) Res.push_back("-fdump-record-layouts"); if (Opts.DumpVTableLayouts) Res.push_back("-fdump-vtable-layouts"); if (Opts.NoBitFieldTypeAlign) Res.push_back("-fno-bitfield-type-alignment"); - if (Opts.PICLevel) { - Res.push_back("-pic-level"); - Res.push_back(llvm::utostr(Opts.PICLevel)); - } + if (Opts.PICLevel) + Res.push_back("-pic-level", llvm::utostr(Opts.PICLevel)); + if (Opts.PIELevel) + Res.push_back("-pie-level", llvm::utostr(Opts.PIELevel)); if (Opts.ObjCGCBitmapPrint) Res.push_back("-print-ivar-layout"); if (Opts.NoConstantCFStrings) @@ -757,77 +787,70 @@ static void LangOptsToArgs(const LangOptions &Opts, if (Opts.InlineVisibilityHidden) Res.push_back("-fvisibility-inlines-hidden"); - if (Opts.getStackProtector() != 0) { - Res.push_back("-stack-protector"); - Res.push_back(llvm::utostr(Opts.getStackProtector())); - } - if (Opts.InstantiationDepth != DefaultLangOpts.InstantiationDepth) { - Res.push_back("-ftemplate-depth"); - Res.push_back(llvm::utostr(Opts.InstantiationDepth)); - } - if (!Opts.ObjCConstantStringClass.empty()) { - Res.push_back("-fconstant-string-class"); - Res.push_back(Opts.ObjCConstantStringClass); - } + if (Opts.getStackProtector() != 0) + Res.push_back("-stack-protector", llvm::utostr(Opts.getStackProtector())); + if (Opts.InstantiationDepth != DefaultLangOpts.InstantiationDepth) + Res.push_back("-ftemplate-depth", llvm::utostr(Opts.InstantiationDepth)); + if (Opts.ConstexprCallDepth != DefaultLangOpts.ConstexprCallDepth) + Res.push_back("-fconstexpr-depth", llvm::utostr(Opts.ConstexprCallDepth)); + if (!Opts.ObjCConstantStringClass.empty()) + Res.push_back("-fconstant-string-class", Opts.ObjCConstantStringClass); if (Opts.FakeAddressSpaceMap) Res.push_back("-ffake-address-space-map"); if (Opts.ParseUnknownAnytype) Res.push_back("-funknown-anytype"); if (Opts.DebuggerSupport) Res.push_back("-fdebugger-support"); + if (Opts.DebuggerCastResultToId) + Res.push_back("-fdebugger-cast-result-to-id"); + if (Opts.DebuggerObjCLiteral) + Res.push_back("-fdebugger-objc-literal"); if (Opts.DelayedTemplateParsing) Res.push_back("-fdelayed-template-parsing"); if (Opts.Deprecated) Res.push_back("-fdeprecated-macro"); + if (Opts.ApplePragmaPack) + Res.push_back("-fapple-pragma-pack"); + if (!Opts.CurrentModule.empty()) + Res.push_back("-fmodule-name=" + Opts.CurrentModule); } static void PreprocessorOptsToArgs(const PreprocessorOptions &Opts, - std::vector<std::string> &Res) { + ToArgsList &Res) { for (unsigned i = 0, e = Opts.Macros.size(); i != e; ++i) Res.push_back(std::string(Opts.Macros[i].second ? "-U" : "-D") + Opts.Macros[i].first); for (unsigned i = 0, e = Opts.Includes.size(); i != e; ++i) { // FIXME: We need to avoid reincluding the implicit PCH and PTH includes. - Res.push_back("-include"); - Res.push_back(Opts.Includes[i]); - } - for (unsigned i = 0, e = Opts.MacroIncludes.size(); i != e; ++i) { - Res.push_back("-imacros"); - Res.push_back(Opts.MacroIncludes[i]); + Res.push_back("-include", Opts.Includes[i]); } + for (unsigned i = 0, e = Opts.MacroIncludes.size(); i != e; ++i) + Res.push_back("-imacros", Opts.MacroIncludes[i]); if (!Opts.UsePredefines) Res.push_back("-undef"); if (Opts.DetailedRecord) Res.push_back("-detailed-preprocessing-record"); - if (!Opts.ImplicitPCHInclude.empty()) { - Res.push_back("-include-pch"); - Res.push_back(Opts.ImplicitPCHInclude); - } - if (!Opts.ImplicitPTHInclude.empty()) { - Res.push_back("-include-pth"); - Res.push_back(Opts.ImplicitPTHInclude); - } + if (!Opts.ImplicitPCHInclude.empty()) + Res.push_back("-include-pch", Opts.ImplicitPCHInclude); + if (!Opts.ImplicitPTHInclude.empty()) + Res.push_back("-include-pth", Opts.ImplicitPTHInclude); if (!Opts.TokenCache.empty()) { - if (Opts.ImplicitPTHInclude.empty()) { - Res.push_back("-token-cache"); - Res.push_back(Opts.TokenCache); - } else + if (Opts.ImplicitPTHInclude.empty()) + Res.push_back("-token-cache", Opts.TokenCache); + else assert(Opts.ImplicitPTHInclude == Opts.TokenCache && "Unsupported option combination!"); } - for (unsigned i = 0, e = Opts.ChainedIncludes.size(); i != e; ++i) { - Res.push_back("-chain-include"); - Res.push_back(Opts.ChainedIncludes[i]); - } + for (unsigned i = 0, e = Opts.ChainedIncludes.size(); i != e; ++i) + Res.push_back("-chain-include", Opts.ChainedIncludes[i]); for (unsigned i = 0, e = Opts.RemappedFiles.size(); i != e; ++i) { - Res.push_back("-remap-file"); - Res.push_back(Opts.RemappedFiles[i].first + ";" + - Opts.RemappedFiles[i].second); + Res.push_back("-remap-file", Opts.RemappedFiles[i].first + ";" + + Opts.RemappedFiles[i].second); } } static void PreprocessorOutputOptsToArgs(const PreprocessorOutputOptions &Opts, - std::vector<std::string> &Res) { + ToArgsList &Res) { if (!Opts.ShowCPP && !Opts.ShowMacros) llvm::report_fatal_error("Invalid option combination!"); @@ -845,43 +868,34 @@ static void PreprocessorOutputOptsToArgs(const PreprocessorOutputOptions &Opts, } static void TargetOptsToArgs(const TargetOptions &Opts, - std::vector<std::string> &Res) { + ToArgsList &Res) { Res.push_back("-triple"); Res.push_back(Opts.Triple); - if (!Opts.CPU.empty()) { - Res.push_back("-target-cpu"); - Res.push_back(Opts.CPU); - } - if (!Opts.ABI.empty()) { - Res.push_back("-target-abi"); - Res.push_back(Opts.ABI); - } - if (!Opts.LinkerVersion.empty()) { - Res.push_back("-target-linker-version"); - Res.push_back(Opts.LinkerVersion); - } - if (!Opts.CXXABI.empty()) { - Res.push_back("-cxx-abi"); - Res.push_back(Opts.CXXABI); - } - for (unsigned i = 0, e = Opts.Features.size(); i != e; ++i) { - Res.push_back("-target-feature"); - Res.push_back(Opts.Features[i]); - } + if (!Opts.CPU.empty()) + Res.push_back("-target-cpu", Opts.CPU); + if (!Opts.ABI.empty()) + Res.push_back("-target-abi", Opts.ABI); + if (!Opts.LinkerVersion.empty()) + Res.push_back("-target-linker-version", Opts.LinkerVersion); + if (!Opts.CXXABI.empty()) + Res.push_back("-cxx-abi", Opts.CXXABI); + for (unsigned i = 0, e = Opts.Features.size(); i != e; ++i) + Res.push_back("-target-feature", Opts.Features[i]); } void CompilerInvocation::toArgs(std::vector<std::string> &Res) { - AnalyzerOptsToArgs(getAnalyzerOpts(), Res); - CodeGenOptsToArgs(getCodeGenOpts(), Res); - DependencyOutputOptsToArgs(getDependencyOutputOpts(), Res); - DiagnosticOptsToArgs(getDiagnosticOpts(), Res); - FileSystemOptsToArgs(getFileSystemOpts(), Res); - FrontendOptsToArgs(getFrontendOpts(), Res); - HeaderSearchOptsToArgs(getHeaderSearchOpts(), Res); - LangOptsToArgs(getLangOpts(), Res); - PreprocessorOptsToArgs(getPreprocessorOpts(), Res); - PreprocessorOutputOptsToArgs(getPreprocessorOutputOpts(), Res); - TargetOptsToArgs(getTargetOpts(), Res); + ToArgsList List(Res); + AnalyzerOptsToArgs(getAnalyzerOpts(), List); + CodeGenOptsToArgs(getCodeGenOpts(), List); + DependencyOutputOptsToArgs(getDependencyOutputOpts(), List); + DiagnosticOptsToArgs(getDiagnosticOpts(), List); + FileSystemOptsToArgs(getFileSystemOpts(), List); + FrontendOptsToArgs(getFrontendOpts(), List); + HeaderSearchOptsToArgs(getHeaderSearchOpts(), List); + LangOptsToArgs(*getLangOpts(), List); + PreprocessorOptsToArgs(getPreprocessorOpts(), List); + PreprocessorOutputOptsToArgs(getPreprocessorOutputOpts(), List); + TargetOptsToArgs(getTargetOpts(), List); } //===----------------------------------------------------------------------===// @@ -903,10 +917,10 @@ static unsigned getOptimizationLevel(ArgList &Args, InputKind IK, Args.getLastArgIntValue(OPT_O, DefaultOpt, Diags); } -static void ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, +static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, DiagnosticsEngine &Diags) { using namespace cc1options; - + bool Success = true; if (Arg *A = Args.getLastArg(OPT_analyzer_store)) { StringRef Name = A->getValue(Args); AnalysisStores Value = llvm::StringSwitch<AnalysisStores>(Name) @@ -914,12 +928,13 @@ static void ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, .Case(CMDFLAG, NAME##Model) #include "clang/Frontend/Analyses.def" .Default(NumStores); - // FIXME: Error handling. - if (Value == NumStores) + if (Value == NumStores) { Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; - else + Success = false; + } else { Opts.AnalysisStoreOpt = Value; + } } if (Arg *A = Args.getLastArg(OPT_analyzer_constraints)) { @@ -929,12 +944,13 @@ static void ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, .Case(CMDFLAG, NAME##Model) #include "clang/Frontend/Analyses.def" .Default(NumConstraints); - // FIXME: Error handling. - if (Value == NumConstraints) + if (Value == NumConstraints) { Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; - else + Success = false; + } else { Opts.AnalysisConstraintsOpt = Value; + } } if (Arg *A = Args.getLastArg(OPT_analyzer_output)) { @@ -944,12 +960,13 @@ static void ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, .Case(CMDFLAG, PD_##NAME) #include "clang/Frontend/Analyses.def" .Default(NUM_ANALYSIS_DIAG_CLIENTS); - // FIXME: Error handling. - if (Value == NUM_ANALYSIS_DIAG_CLIENTS) + if (Value == NUM_ANALYSIS_DIAG_CLIENTS) { Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; - else + Success = false; + } else { Opts.AnalysisDiagOpt = Value; + } } if (Arg *A = Args.getLastArg(OPT_analyzer_purge)) { @@ -959,17 +976,51 @@ static void ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, .Case(CMDFLAG, NAME) #include "clang/Frontend/Analyses.def" .Default(NumPurgeModes); - // FIXME: Error handling. - if (Value == NumPurgeModes) + if (Value == NumPurgeModes) { Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; - else + Success = false; + } else { Opts.AnalysisPurgeOpt = Value; + } + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_ipa)) { + StringRef Name = A->getValue(Args); + AnalysisIPAMode Value = llvm::StringSwitch<AnalysisIPAMode>(Name) +#define ANALYSIS_IPA(NAME, CMDFLAG, DESC) \ + .Case(CMDFLAG, NAME) +#include "clang/Frontend/Analyses.def" + .Default(NumIPAModes); + if (Value == NumIPAModes) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.IPAMode = Value; + } + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_inlining_mode)) { + StringRef Name = A->getValue(Args); + AnalysisInliningMode Value = llvm::StringSwitch<AnalysisInliningMode>(Name) +#define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC) \ + .Case(CMDFLAG, NAME) +#include "clang/Frontend/Analyses.def" + .Default(NumInliningModes); + if (Value == NumInliningModes) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.InliningMode = Value; + } } Opts.ShowCheckerHelp = Args.hasArg(OPT_analyzer_checker_help); Opts.VisualizeEGDot = Args.hasArg(OPT_analyzer_viz_egraph_graphviz); Opts.VisualizeEGUbi = Args.hasArg(OPT_analyzer_viz_egraph_ubigraph); + Opts.NoRetryExhausted = Args.hasArg(OPT_analyzer_disable_retry_exhausted); Opts.AnalyzeAll = Args.hasArg(OPT_analyzer_opt_analyze_headers); Opts.AnalyzerDisplayProgress = Args.hasArg(OPT_analyzer_display_progress); Opts.AnalyzeNestedBlocks = @@ -983,7 +1034,13 @@ static void ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, Opts.MaxNodes = Args.getLastArgIntValue(OPT_analyzer_max_nodes, 150000,Diags); Opts.MaxLoop = Args.getLastArgIntValue(OPT_analyzer_max_loop, 4, Diags); Opts.EagerlyTrimEGraph = !Args.hasArg(OPT_analyzer_no_eagerly_trim_egraph); - Opts.InlineCall = Args.hasArg(OPT_analyzer_inline_call); + Opts.PrintStats = Args.hasArg(OPT_analyzer_stats); + Opts.InlineMaxStackDepth = + Args.getLastArgIntValue(OPT_analyzer_inline_max_stack_depth, + Opts.InlineMaxStackDepth, Diags); + Opts.InlineMaxFunctionSize = + Args.getLastArgIntValue(OPT_analyzer_inline_max_function_size, + Opts.InlineMaxFunctionSize, Diags); Opts.CheckersControlList.clear(); for (arg_iterator it = Args.filtered_begin(OPT_analyzer_checker, @@ -1000,25 +1057,41 @@ static void ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, for (unsigned i = 0, e = checkers.size(); i != e; ++i) Opts.CheckersControlList.push_back(std::make_pair(checkers[i], enable)); } + + return Success; } -static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, +static bool ParseMigratorArgs(MigratorOptions &Opts, ArgList &Args) { + Opts.NoNSAllocReallocError = Args.hasArg(OPT_migrator_no_nsalloc_error); + Opts.NoFinalizeRemoval = Args.hasArg(OPT_migrator_no_finalize_removal); + return true; +} + +static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, DiagnosticsEngine &Diags) { using namespace cc1options; + bool Success = true; - Opts.OptimizationLevel = getOptimizationLevel(Args, IK, Diags); - if (Opts.OptimizationLevel > 3) { + unsigned OptLevel = getOptimizationLevel(Args, IK, Diags); + if (OptLevel > 3) { Diags.Report(diag::err_drv_invalid_value) - << Args.getLastArg(OPT_O)->getAsString(Args) << Opts.OptimizationLevel; - Opts.OptimizationLevel = 3; + << Args.getLastArg(OPT_O)->getAsString(Args) << OptLevel; + OptLevel = 3; + Success = false; } + Opts.OptimizationLevel = OptLevel; // We must always run at least the always inlining pass. Opts.Inlining = (Opts.OptimizationLevel > 1) ? CodeGenOptions::NormalInlining : CodeGenOptions::OnlyAlwaysInlining; + // -fno-inline-functions overrides OptimizationLevel > 1. + Opts.NoInline = Args.hasArg(OPT_fno_inline); + Opts.Inlining = Args.hasArg(OPT_fno_inline_functions) ? + CodeGenOptions::OnlyAlwaysInlining : Opts.Inlining; Opts.DebugInfo = Args.hasArg(OPT_g); - Opts.LimitDebugInfo = Args.hasArg(OPT_flimit_debug_info); + Opts.LimitDebugInfo = !Args.hasArg(OPT_fno_limit_debug_info) + || Args.hasArg(OPT_flimit_debug_info); Opts.DisableLLVMOpts = Args.hasArg(OPT_disable_llvm_optzns); Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone); Opts.ForbidGuardVariables = Args.hasArg(OPT_fforbid_guard_variables); @@ -1046,12 +1119,17 @@ static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.CodeModel = Args.getLastArgValue(OPT_mcode_model); Opts.DebugPass = Args.getLastArgValue(OPT_mdebug_pass); Opts.DisableFPElim = Args.hasArg(OPT_mdisable_fp_elim); + Opts.DisableTailCalls = Args.hasArg(OPT_mdisable_tail_calls); Opts.FloatABI = Args.getLastArgValue(OPT_mfloat_abi); Opts.HiddenWeakVTables = Args.hasArg(OPT_fhidden_weak_vtables); Opts.LessPreciseFPMAD = Args.hasArg(OPT_cl_mad_enable); Opts.LimitFloatPrecision = Args.getLastArgValue(OPT_mlimit_float_precision); - Opts.NoInfsFPMath = Opts.NoNaNsFPMath = Args.hasArg(OPT_cl_finite_math_only)|| - Args.hasArg(OPT_cl_fast_relaxed_math); + Opts.NoInfsFPMath = (Args.hasArg(OPT_menable_no_infinities) || + Args.hasArg(OPT_cl_finite_math_only)|| + Args.hasArg(OPT_cl_fast_relaxed_math)); + Opts.NoNaNsFPMath = (Args.hasArg(OPT_menable_no_nans) || + Args.hasArg(OPT_cl_finite_math_only)|| + Args.hasArg(OPT_cl_fast_relaxed_math)); Opts.NoZeroInitializedInBSS = Args.hasArg(OPT_mno_zero_initialized_in_bss); Opts.BackendOptions = Args.getAllArgValues(OPT_backend_option); Opts.NumRegisterParameters = Args.getLastArgIntValue(OPT_mregparm, 0, Diags); @@ -1061,11 +1139,15 @@ static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.OmitLeafFramePointer = Args.hasArg(OPT_momit_leaf_frame_pointer); Opts.SaveTempLabels = Args.hasArg(OPT_msave_temp_labels); Opts.NoDwarf2CFIAsm = Args.hasArg(OPT_fno_dwarf2_cfi_asm); + Opts.NoDwarfDirectoryAsm = Args.hasArg(OPT_fno_dwarf_directory_asm); Opts.SoftFloat = Args.hasArg(OPT_msoft_float); - Opts.UnsafeFPMath = Args.hasArg(OPT_cl_unsafe_math_optimizations) || + Opts.StrictEnums = Args.hasArg(OPT_fstrict_enums); + Opts.UnsafeFPMath = Args.hasArg(OPT_menable_unsafe_fp_math) || + Args.hasArg(OPT_cl_unsafe_math_optimizations) || Args.hasArg(OPT_cl_fast_relaxed_math); Opts.UnwindTables = Args.hasArg(OPT_munwind_tables); Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic"); + Opts.TrapFuncName = Args.getLastArgValue(OPT_ftrap_function_EQ); Opts.FunctionSections = Args.hasArg(OPT_ffunction_sections); Opts.DataSections = Args.hasArg(OPT_fdata_sections); @@ -1078,6 +1160,13 @@ static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.EmitGcovArcs = Args.hasArg(OPT_femit_coverage_data); Opts.EmitGcovNotes = Args.hasArg(OPT_femit_coverage_notes); Opts.CoverageFile = Args.getLastArgValue(OPT_coverage_file); + Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); + Opts.LinkBitcodeFile = Args.getLastArgValue(OPT_mlink_bitcode_file); + Opts.StackRealignment = Args.hasArg(OPT_mstackrealign); + if (Arg *A = Args.getLastArg(OPT_mstack_alignment)) { + StringRef Val = A->getValue(Args); + Val.getAsInteger(10, Opts.StackAlignment); + } if (Arg *A = Args.getLastArg(OPT_fobjc_dispatch_method_EQ)) { StringRef Name = A->getValue(Args); @@ -1086,11 +1175,15 @@ static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, .Case("non-legacy", CodeGenOptions::NonLegacy) .Case("mixed", CodeGenOptions::Mixed) .Default(~0U); - if (Method == ~0U) + if (Method == ~0U) { Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; - else + Success = false; + } else { Opts.ObjCDispatchMethod = Method; + } } + + return Success; } static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, @@ -1103,12 +1196,17 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, Opts.ShowHeaderIncludes = Args.hasArg(OPT_H); Opts.HeaderIncludeOutputFile = Args.getLastArgValue(OPT_header_include_file); Opts.AddMissingHeaderDeps = Args.hasArg(OPT_MG); + Opts.DOTOutputFile = Args.getLastArgValue(OPT_dependency_dot); } -static void ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, - DiagnosticsEngine &Diags) { +bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, + DiagnosticsEngine *Diags) { using namespace cc1options; + bool Success = true; + Opts.DiagnosticLogFile = Args.getLastArgValue(OPT_diagnostic_log_file); + Opts.DiagnosticSerializationFile = + Args.getLastArgValue(OPT_diagnostic_serialized_file); Opts.IgnoreWarnings = Args.hasArg(OPT_w); Opts.NoRewriteMacros = Args.hasArg(OPT_Wno_rewrite_macros); Opts.Pedantic = Args.hasArg(OPT_pedantic); @@ -1120,7 +1218,6 @@ static void ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, /*Default=*/true); Opts.ShowFixits = !Args.hasArg(OPT_fno_diagnostics_fixit_info); Opts.ShowLocation = !Args.hasArg(OPT_fno_show_source_location); - Opts.ShowNames = Args.hasArg(OPT_fdiagnostics_show_name); Opts.ShowOptionNames = Args.hasArg(OPT_fdiagnostics_show_option); // Default behavior is to not to show note include stacks. @@ -1136,10 +1233,13 @@ static void ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.ShowOverloads = DiagnosticsEngine::Ovl_Best; else if (ShowOverloads == "all") Opts.ShowOverloads = DiagnosticsEngine::Ovl_All; - else - Diags.Report(diag::err_drv_invalid_value) + else { + Success = false; + if (Diags) + Diags->Report(diag::err_drv_invalid_value) << Args.getLastArg(OPT_fshow_overloads_EQ)->getAsString(Args) << ShowOverloads; + } StringRef ShowCategory = Args.getLastArgValue(OPT_fdiagnostics_show_category, "none"); @@ -1149,23 +1249,29 @@ static void ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.ShowCategories = 1; else if (ShowCategory == "name") Opts.ShowCategories = 2; - else - Diags.Report(diag::err_drv_invalid_value) + else { + Success = false; + if (Diags) + Diags->Report(diag::err_drv_invalid_value) << Args.getLastArg(OPT_fdiagnostics_show_category)->getAsString(Args) << ShowCategory; + } StringRef Format = Args.getLastArgValue(OPT_fdiagnostics_format, "clang"); if (Format == "clang") Opts.Format = DiagnosticOptions::Clang; - else if (Format == "msvc") + else if (Format == "msvc") Opts.Format = DiagnosticOptions::Msvc; - else if (Format == "vi") + else if (Format == "vi") Opts.Format = DiagnosticOptions::Vi; - else - Diags.Report(diag::err_drv_invalid_value) + else { + Success = false; + if (Diags) + Diags->Report(diag::err_drv_invalid_value) << Args.getLastArg(OPT_fdiagnostics_format)->getAsString(Args) << Format; + } Opts.ShowSourceRanges = Args.hasArg(OPT_fdiagnostics_print_source_range_info); Opts.ShowParseableFixits = Args.hasArg(OPT_fdiagnostics_parseable_fixits); @@ -1178,16 +1284,32 @@ static void ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, = Args.getLastArgIntValue(OPT_ftemplate_backtrace_limit, DiagnosticOptions::DefaultTemplateBacktraceLimit, Diags); + Opts.ConstexprBacktraceLimit + = Args.getLastArgIntValue(OPT_fconstexpr_backtrace_limit, + DiagnosticOptions::DefaultConstexprBacktraceLimit, + Diags); Opts.TabStop = Args.getLastArgIntValue(OPT_ftabstop, DiagnosticOptions::DefaultTabStop, Diags); if (Opts.TabStop == 0 || Opts.TabStop > DiagnosticOptions::MaxTabStop) { - Diags.Report(diag::warn_ignoring_ftabstop_value) - << Opts.TabStop << DiagnosticOptions::DefaultTabStop; Opts.TabStop = DiagnosticOptions::DefaultTabStop; + if (Diags) + Diags->Report(diag::warn_ignoring_ftabstop_value) + << Opts.TabStop << DiagnosticOptions::DefaultTabStop; } Opts.MessageLength = Args.getLastArgIntValue(OPT_fmessage_length, 0, Diags); Opts.DumpBuildInformation = Args.getLastArgValue(OPT_dump_build_information); - Opts.Warnings = Args.getAllArgValues(OPT_W); + + for (arg_iterator it = Args.filtered_begin(OPT_W), + ie = Args.filtered_end(); it != ie; ++it) { + StringRef V = (*it)->getValue(Args); + // "-Wl," and such are not warnings options. + if (V.startswith("l,") || V.startswith("a,") || V.startswith("p,")) + continue; + + Opts.Warnings.push_back(V); + } + + return Success; } static void ParseFileSystemArgs(FileSystemOptions &Opts, ArgList &Args) { @@ -1249,6 +1371,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ProgramAction = frontend::PrintPreamble; break; case OPT_E: Opts.ProgramAction = frontend::PrintPreprocessedInput; break; + case OPT_pubnames_dump: + Opts.ProgramAction = frontend::PubnamesDump; break; case OPT_rewrite_macros: Opts.ProgramAction = frontend::RewriteMacros; break; case OPT_rewrite_objc: @@ -1257,6 +1381,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ProgramAction = frontend::RewriteTest; break; case OPT_analyze: Opts.ProgramAction = frontend::RunAnalysis; break; + case OPT_migrate: + Opts.ProgramAction = frontend::MigrateSource; break; case OPT_Eonly: Opts.ProgramAction = frontend::RunPreprocessorOnly; break; } @@ -1308,8 +1434,11 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ASTMergeFiles = Args.getAllArgValues(OPT_ast_merge); Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm); Opts.FixWhatYouCan = Args.hasArg(OPT_fix_what_you_can); - - Opts.ARCMTAction = FrontendOptions::ARCMT_None; + Opts.FixOnlyWarnings = Args.hasArg(OPT_fix_only_warnings); + Opts.FixAndRecompile = Args.hasArg(OPT_fixit_recompile); + Opts.FixToTemporaries = Args.hasArg(OPT_fixit_to_temp); + Opts.OverrideRecordLayoutsFile + = Args.getLastArgValue(OPT_foverride_record_layout_EQ); if (const Arg *A = Args.getLastArg(OPT_arcmt_check, OPT_arcmt_modify, OPT_arcmt_migrate)) { @@ -1327,12 +1456,23 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, break; } } - Opts.ARCMTMigrateDir = Args.getLastArgValue(OPT_arcmt_migrate_directory); + Opts.MTMigrateDir = Args.getLastArgValue(OPT_mt_migrate_directory); Opts.ARCMTMigrateReportOut = Args.getLastArgValue(OPT_arcmt_migrate_report_output); Opts.ARCMTMigrateEmitARCErrors = Args.hasArg(OPT_arcmt_migrate_emit_arc_errors); + if (Args.hasArg(OPT_objcmt_migrate_literals)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Literals; + if (Args.hasArg(OPT_objcmt_migrate_subscripting)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Subscripting; + + if (Opts.ARCMTAction != FrontendOptions::ARCMT_None && + Opts.ObjCMTAction != FrontendOptions::ObjCMT_None) { + Diags.Report(diag::err_drv_argument_not_allowed_with) + << "ARC migration" << "ObjC migration"; + } + InputKind DashX = IK_None; if (const Arg *A = Args.getLastArg(OPT_x)) { DashX = llvm::StringSwitch<InputKind>(A->getValue(Args)) @@ -1376,7 +1516,7 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, if (i == 0) DashX = IK; } - Opts.Inputs.push_back(std::make_pair(IK, Inputs[i])); + Opts.Inputs.push_back(FrontendInputFile(Inputs[i], IK)); } return DashX; @@ -1457,6 +1597,10 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { OPT_iwithsysroot), ie = Args.filtered_end(); it != ie; ++it) Opts.AddPath((*it)->getValue(Args), frontend::System, true, false, !(*it)->getOption().matches(OPT_iwithsysroot)); + for (arg_iterator it = Args.filtered_begin(OPT_iframework), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(Args), frontend::System, true, true, + true); // Add the paths for the various language specific isystem flags. for (arg_iterator it = Args.filtered_begin(OPT_c_isystem), @@ -1529,7 +1673,7 @@ void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); Opts.BCPLComment = Std.hasBCPLComments(); Opts.C99 = Std.isC99(); - Opts.C1X = Std.isC1X(); + Opts.C11 = Std.isC11(); Opts.CPlusPlus = Std.isCPlusPlus(); Opts.CPlusPlus0x = Std.isCPlusPlus0x(); Opts.Digraphs = Std.hasDigraphs(); @@ -1680,6 +1824,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, else if (Vis == "hidden") Opts.setVisibilityMode(HiddenVisibility); else if (Vis == "protected") + // FIXME: diagnose if target does not support protected visibility Opts.setVisibilityMode(ProtectedVisibility); else Diags.Report(diag::err_drv_invalid_value) @@ -1704,7 +1849,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, OPT_fno_dollars_in_identifiers, Opts.DollarIdents); Opts.PascalStrings = Args.hasArg(OPT_fpascal_strings); - Opts.MicrosoftExt = Args.hasArg(OPT_fms_extensions); + Opts.MicrosoftExt + = Args.hasArg(OPT_fms_extensions) || Args.hasArg(OPT_fms_compatibility); Opts.MicrosoftMode = Args.hasArg(OPT_fms_compatibility); Opts.MSCVersion = Args.getLastArgIntValue(OPT_fmsc_version, 0, Diags); Opts.Borland = Args.hasArg(OPT_fborland_extensions); @@ -1724,6 +1870,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.RTTI = !Args.hasArg(OPT_fno_rtti); Opts.Blocks = Args.hasArg(OPT_fblocks); Opts.BlocksRuntimeOptional = Args.hasArg(OPT_fblocks_runtime_optional); + Opts.Modules = Args.hasArg(OPT_fmodules); Opts.CharIsSigned = !Args.hasArg(OPT_fno_signed_char); Opts.ShortWChar = Args.hasArg(OPT_fshort_wchar); Opts.ShortEnums = Args.hasArg(OPT_fshort_enums); @@ -1736,7 +1883,9 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); Opts.MathErrno = Args.hasArg(OPT_fmath_errno); Opts.InstantiationDepth = Args.getLastArgIntValue(OPT_ftemplate_depth, 1024, - Diags); + Diags); + Opts.ConstexprCallDepth = Args.getLastArgIntValue(OPT_fconstexpr_depth, 512, + Diags); Opts.DelayedTemplateParsing = Args.hasArg(OPT_fdelayed_template_parsing); Opts.NumLargeByValueCopy = Args.getLastArgIntValue(OPT_Wlarge_by_value_copy, 0, Diags); @@ -1753,18 +1902,27 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls); Opts.PackStruct = Args.getLastArgIntValue(OPT_fpack_struct, 0, Diags); Opts.PICLevel = Args.getLastArgIntValue(OPT_pic_level, 0, Diags); + Opts.PIELevel = Args.getLastArgIntValue(OPT_pie_level, 0, Diags); Opts.Static = Args.hasArg(OPT_static_define); - Opts.DumpRecordLayouts = Args.hasArg(OPT_fdump_record_layouts); + Opts.DumpRecordLayoutsSimple = Args.hasArg(OPT_fdump_record_layouts_simple); + Opts.DumpRecordLayouts = Opts.DumpRecordLayoutsSimple + || Args.hasArg(OPT_fdump_record_layouts); Opts.DumpVTableLayouts = Args.hasArg(OPT_fdump_vtable_layouts); Opts.SpellChecking = !Args.hasArg(OPT_fno_spell_checking); Opts.NoBitFieldTypeAlign = Args.hasArg(OPT_fno_bitfield_type_align); Opts.SinglePrecisionConstants = Args.hasArg(OPT_cl_single_precision_constant); Opts.FastRelaxedMath = Args.hasArg(OPT_cl_fast_relaxed_math); - Opts.OptimizeSize = 0; Opts.MRTD = Args.hasArg(OPT_mrtd); + Opts.HexagonQdsp6Compat = Args.hasArg(OPT_mqdsp6_compat); Opts.FakeAddressSpaceMap = Args.hasArg(OPT_ffake_address_space_map); Opts.ParseUnknownAnytype = Args.hasArg(OPT_funknown_anytype); Opts.DebuggerSupport = Args.hasArg(OPT_fdebugger_support); + Opts.DebuggerCastResultToId = Args.hasArg(OPT_fdebugger_cast_result_to_id); + Opts.DebuggerObjCLiteral = Args.hasArg(OPT_fdebugger_objc_literal); + Opts.AddressSanitizer = Args.hasArg(OPT_faddress_sanitizer); + Opts.ThreadSanitizer = Args.hasArg(OPT_fthread_sanitizer); + Opts.ApplePragmaPack = Args.hasArg(OPT_fapple_pragma_pack); + Opts.CurrentModule = Args.getLastArgValue(OPT_fmodule_name); // Record whether the __DEPRECATED define was requested. Opts.Deprecated = Args.hasFlag(OPT_fdeprecated_macro, @@ -1774,13 +1932,14 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, // FIXME: Eliminate this dependency. unsigned Opt = getOptimizationLevel(Args, IK, Diags); Opts.Optimize = Opt != 0; + Opts.OptimizeSize = Args.hasArg(OPT_Os) || Args.hasArg(OPT_Oz); // This is the __NO_INLINE__ define, which just depends on things like the // optimization level and -fno-inline, not actually whether the backend has // inlining enabled. - // - // FIXME: This is affected by other options (-fno-inline). - Opts.NoInline = !Opt; + Opts.NoInlineDefine = !Opt || Args.hasArg(OPT_fno_inline); + + Opts.FastMath = Args.hasArg(OPT_ffast_math); unsigned SSP = Args.getLastArgIntValue(OPT_stack_protector, 0, Diags); switch (SSP) { @@ -1806,7 +1965,6 @@ static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, Opts.TokenCache = Opts.ImplicitPTHInclude; Opts.UsePredefines = !Args.hasArg(OPT_undef); Opts.DetailedRecord = Args.hasArg(OPT_detailed_preprocessing_record); - Opts.AutoModuleImport = Args.hasArg(OPT_fauto_module_import); Opts.DisablePCHValidation = Args.hasArg(OPT_fno_validate_pch); Opts.DumpDeserializedPCHDecls = Args.hasArg(OPT_dump_deserialized_pch_decls); @@ -1917,45 +2075,54 @@ static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args) { Opts.LinkerVersion = Args.getLastArgValue(OPT_target_linker_version); Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)); - // Use the host triple if unspecified. + // Use the default target triple if unspecified. if (Opts.Triple.empty()) - Opts.Triple = llvm::sys::getHostTriple(); + Opts.Triple = llvm::sys::getDefaultTargetTriple(); } // -void CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, +bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, const char *const *ArgBegin, const char *const *ArgEnd, DiagnosticsEngine &Diags) { + bool Success = true; + // Parse the arguments. - llvm::OwningPtr<OptTable> Opts(createCC1OptTable()); + OwningPtr<OptTable> Opts(createCC1OptTable()); unsigned MissingArgIndex, MissingArgCount; - llvm::OwningPtr<InputArgList> Args( + OwningPtr<InputArgList> Args( Opts->ParseArgs(ArgBegin, ArgEnd,MissingArgIndex, MissingArgCount)); // Check for missing argument error. - if (MissingArgCount) + if (MissingArgCount) { Diags.Report(diag::err_drv_missing_argument) << Args->getArgString(MissingArgIndex) << MissingArgCount; + Success = false; + } // Issue errors on unknown arguments. for (arg_iterator it = Args->filtered_begin(OPT_UNKNOWN), - ie = Args->filtered_end(); it != ie; ++it) + ie = Args->filtered_end(); it != ie; ++it) { Diags.Report(diag::err_drv_unknown_argument) << (*it)->getAsString(*Args); + Success = false; + } - ParseAnalyzerArgs(Res.getAnalyzerOpts(), *Args, Diags); + Success = ParseAnalyzerArgs(Res.getAnalyzerOpts(), *Args, Diags) && Success; + Success = ParseMigratorArgs(Res.getMigratorOpts(), *Args) && Success; ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), *Args); - ParseDiagnosticArgs(Res.getDiagnosticOpts(), *Args, Diags); + Success = ParseDiagnosticArgs(Res.getDiagnosticOpts(), *Args, &Diags) + && Success; ParseFileSystemArgs(Res.getFileSystemOpts(), *Args); // FIXME: We shouldn't have to pass the DashX option around here InputKind DashX = ParseFrontendArgs(Res.getFrontendOpts(), *Args, Diags); - ParseCodeGenArgs(Res.getCodeGenOpts(), *Args, DashX, Diags); + Success = ParseCodeGenArgs(Res.getCodeGenOpts(), *Args, DashX, Diags) + && Success; ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), *Args); if (DashX != IK_AST && DashX != IK_LLVM_IR) { - ParseLangArgs(Res.getLangOpts(), *Args, DashX, Diags); + ParseLangArgs(*Res.getLangOpts(), *Args, DashX, Diags); if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) - Res.getLangOpts().ObjCExceptions = 1; + Res.getLangOpts()->ObjCExceptions = 1; } // FIXME: ParsePreprocessorArgs uses the FileManager to read the contents of // PCH file and find the original header name. Remove the need to do that in @@ -1965,6 +2132,8 @@ void CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ParsePreprocessorArgs(Res.getPreprocessorOpts(), *Args, FileMgr, Diags); ParsePreprocessorOutputArgs(Res.getPreprocessorOutputOpts(), *Args); ParseTargetArgs(Res.getTargetOpts(), *Args); + + return Success; } namespace { @@ -2024,13 +2193,16 @@ std::string CompilerInvocation::getModuleHash() const { ModuleSignature Signature; // Start the signature with the compiler version. - Signature.add(getClangFullRepositoryVersion()); + // FIXME: The full version string can be quite long. Omit it from the + // module hash for now to avoid failures where the path name becomes too + // long. An MD5 or similar checksum would work well here. + // Signature.add(getClangFullRepositoryVersion()); // Extend the signature with the language options #define LANGOPT(Name, Bits, Default, Description) \ - Signature.add(LangOpts.Name, Bits); + Signature.add(LangOpts->Name, Bits); #define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ - Signature.add(static_cast<unsigned>(LangOpts.get##Name()), Bits); + Signature.add(static_cast<unsigned>(LangOpts->get##Name()), Bits); #define BENIGN_LANGOPT(Name, Bits, Default, Description) #define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description) #include "clang/Basic/LangOptions.def" diff --git a/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp b/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp index fc15081..b477ade 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -30,7 +30,7 @@ using namespace clang; /// argument vector. CompilerInvocation * clang::createInvocationFromCommandLine(ArrayRef<const char *> ArgList, - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags) { + IntrusiveRefCntPtr<DiagnosticsEngine> Diags) { if (!Diags.getPtr()) { // No diagnostics engine was provided, so create our own diagnostics object // with the default options. @@ -48,13 +48,13 @@ clang::createInvocationFromCommandLine(ArrayRef<const char *> ArgList, Args.push_back("-fsyntax-only"); // FIXME: We shouldn't have to pass in the path info. - driver::Driver TheDriver("clang", llvm::sys::getHostTriple(), + driver::Driver TheDriver("clang", llvm::sys::getDefaultTargetTriple(), "a.out", false, *Diags); // Don't check that inputs exist, they may have been remapped. TheDriver.setCheckInputsExist(false); - llvm::OwningPtr<driver::Compilation> C(TheDriver.BuildCompilation(Args)); + OwningPtr<driver::Compilation> C(TheDriver.BuildCompilation(Args)); // Just print the cc1 options if -### was present. if (C->getArgs().hasArg(driver::options::OPT__HASH_HASH_HASH)) { @@ -66,7 +66,7 @@ clang::createInvocationFromCommandLine(ArrayRef<const char *> ArgList, // failed. const driver::JobList &Jobs = C->getJobs(); if (Jobs.size() != 1 || !isa<driver::Command>(*Jobs.begin())) { - llvm::SmallString<256> Msg; + SmallString<256> Msg; llvm::raw_svector_ostream OS(Msg); C->PrintJob(OS, C->getJobs(), "; ", true); Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); @@ -80,11 +80,12 @@ clang::createInvocationFromCommandLine(ArrayRef<const char *> ArgList, } const driver::ArgStringList &CCArgs = Cmd->getArguments(); - CompilerInvocation *CI = new CompilerInvocation(); - CompilerInvocation::CreateFromArgs(*CI, + OwningPtr<CompilerInvocation> CI(new CompilerInvocation()); + if (!CompilerInvocation::CreateFromArgs(*CI, const_cast<const char **>(CCArgs.data()), const_cast<const char **>(CCArgs.data()) + CCArgs.size(), - *Diags); - return CI; + *Diags)) + return 0; + return CI.take(); } diff --git a/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp b/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp index ff3a123..21f5daa 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp @@ -31,11 +31,12 @@ class DependencyFileCallback : public PPCallbacks { std::vector<std::string> Files; llvm::StringSet<> FilesSet; const Preprocessor *PP; + std::string OutputFile; std::vector<std::string> Targets; - raw_ostream *OS; bool IncludeSystemHeaders; bool PhonyTarget; bool AddMissingHeaderDeps; + bool SeenMissingHeader; private: bool FileMatchesDepCriteria(const char *Filename, SrcMgr::CharacteristicKind FileType); @@ -44,12 +45,12 @@ private: public: DependencyFileCallback(const Preprocessor *_PP, - raw_ostream *_OS, const DependencyOutputOptions &Opts) - : PP(_PP), Targets(Opts.Targets), OS(_OS), + : PP(_PP), OutputFile(Opts.OutputFile), Targets(Opts.Targets), IncludeSystemHeaders(Opts.IncludeSystemHeaders), PhonyTarget(Opts.UsePhonyTargets), - AddMissingHeaderDeps(Opts.AddMissingHeaderDeps) {} + AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), + SeenMissingHeader(false) {} virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, @@ -65,8 +66,6 @@ public: virtual void EndOfMainFile() { OutputDependencyFile(); - delete OS; - OS = 0; } }; } @@ -78,19 +77,11 @@ void clang::AttachDependencyFileGen(Preprocessor &PP, return; } - std::string Err; - raw_ostream *OS(new llvm::raw_fd_ostream(Opts.OutputFile.c_str(), Err)); - if (!Err.empty()) { - PP.getDiagnostics().Report(diag::err_fe_error_opening) - << Opts.OutputFile << Err; - return; - } - // Disable the "file not found" diagnostic if the -MG option was given. if (Opts.AddMissingHeaderDeps) PP.SetSuppressIncludeNotFoundError(true); - PP.addPPCallbacks(new DependencyFileCallback(&PP, OS, Opts)); + PP.addPPCallbacks(new DependencyFileCallback(&PP, Opts)); } /// FileMatchesDepCriteria - Determine whether the given Filename should be @@ -145,8 +136,12 @@ void DependencyFileCallback::InclusionDirective(SourceLocation HashLoc, SourceLocation EndLoc, StringRef SearchPath, StringRef RelativePath) { - if (AddMissingHeaderDeps && !File) - AddFilename(FileName); + if (!File) { + if (AddMissingHeaderDeps) + AddFilename(FileName); + else + SeenMissingHeader = true; + } } void DependencyFileCallback::AddFilename(StringRef Filename) { @@ -165,6 +160,19 @@ static void PrintFilename(raw_ostream &OS, StringRef Filename) { } void DependencyFileCallback::OutputDependencyFile() { + if (SeenMissingHeader) { + llvm::sys::Path(OutputFile).eraseFromDisk(); + return; + } + + std::string Err; + llvm::raw_fd_ostream OS(OutputFile.c_str(), Err); + if (!Err.empty()) { + PP->getDiagnostics().Report(diag::err_fe_error_opening) + << OutputFile << Err; + return; + } + // Write out the dependency targets, trying to avoid overly long // lines when possible. We try our best to emit exactly the same // dependency file as GCC (4.2), assuming the included files are the @@ -179,16 +187,16 @@ void DependencyFileCallback::OutputDependencyFile() { Columns += N; } else if (Columns + N + 2 > MaxColumns) { Columns = N + 2; - *OS << " \\\n "; + OS << " \\\n "; } else { Columns += N + 1; - *OS << ' '; + OS << ' '; } // Targets already quoted as needed. - *OS << *I; + OS << *I; } - *OS << ':'; + OS << ':'; Columns += 1; // Now add each dependency in the order it was seen, but avoiding @@ -200,23 +208,23 @@ void DependencyFileCallback::OutputDependencyFile() { // break the line on the next iteration. unsigned N = I->length(); if (Columns + (N + 1) + 2 > MaxColumns) { - *OS << " \\\n "; + OS << " \\\n "; Columns = 2; } - *OS << ' '; - PrintFilename(*OS, *I); + OS << ' '; + PrintFilename(OS, *I); Columns += N + 1; } - *OS << '\n'; + OS << '\n'; // Create phony targets if requested. if (PhonyTarget && !Files.empty()) { // Skip the first entry, this is always the input file itself. for (std::vector<std::string>::iterator I = Files.begin() + 1, E = Files.end(); I != E; ++I) { - *OS << '\n'; - PrintFilename(*OS, *I); - *OS << ":\n"; + OS << '\n'; + PrintFilename(OS, *I); + OS << ":\n"; } } } diff --git a/contrib/llvm/tools/clang/lib/Frontend/DependencyGraph.cpp b/contrib/llvm/tools/clang/lib/Frontend/DependencyGraph.cpp new file mode 100644 index 0000000..eebaf0c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DependencyGraph.cpp @@ -0,0 +1,140 @@ +//===--- DependencyGraph.cpp - Generate dependency file -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code generates a header dependency graph in DOT format, for use +// with, e.g., GraphViz. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/GraphWriter.h" + +using namespace clang; +namespace DOT = llvm::DOT; + +namespace { +class DependencyGraphCallback : public PPCallbacks { + const Preprocessor *PP; + std::string OutputFile; + std::string SysRoot; + llvm::SetVector<const FileEntry *> AllFiles; + typedef llvm::DenseMap<const FileEntry *, + llvm::SmallVector<const FileEntry *, 2> > + DependencyMap; + + DependencyMap Dependencies; + +private: + llvm::raw_ostream &writeNodeReference(llvm::raw_ostream &OS, + const FileEntry *Node); + void OutputGraphFile(); + +public: + DependencyGraphCallback(const Preprocessor *_PP, StringRef OutputFile, + StringRef SysRoot) + : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) { } + + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + const FileEntry *File, + SourceLocation EndLoc, + StringRef SearchPath, + StringRef RelativePath); + + virtual void EndOfMainFile() { + OutputGraphFile(); + } + +}; +} + +void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile, + StringRef SysRoot) { + PP.addPPCallbacks(new DependencyGraphCallback(&PP, OutputFile, SysRoot)); +} + +void DependencyGraphCallback::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + const FileEntry *File, + SourceLocation EndLoc, + StringRef SearchPath, + StringRef RelativePath) { + if (!File) + return; + + SourceManager &SM = PP->getSourceManager(); + const FileEntry *FromFile + = SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(HashLoc))); + if (FromFile == 0) + return; + + Dependencies[FromFile].push_back(File); + + AllFiles.insert(File); + AllFiles.insert(FromFile); +} + +llvm::raw_ostream & +DependencyGraphCallback::writeNodeReference(llvm::raw_ostream &OS, + const FileEntry *Node) { + OS << "header_" << Node->getUID(); + return OS; +} + +void DependencyGraphCallback::OutputGraphFile() { + std::string Err; + llvm::raw_fd_ostream OS(OutputFile.c_str(), Err); + if (!Err.empty()) { + PP->getDiagnostics().Report(diag::err_fe_error_opening) + << OutputFile << Err; + return; + } + + OS << "digraph \"dependencies\" {\n"; + + // Write the nodes + for (unsigned I = 0, N = AllFiles.size(); I != N; ++I) { + // Write the node itself. + OS.indent(2); + writeNodeReference(OS, AllFiles[I]); + OS << " [ shape=\"box\", label=\""; + StringRef FileName = AllFiles[I]->getName(); + if (FileName.startswith(SysRoot)) + FileName = FileName.substr(SysRoot.size()); + + OS << DOT::EscapeString(FileName) + << "\"];\n"; + } + + // Write the edges + for (DependencyMap::iterator F = Dependencies.begin(), + FEnd = Dependencies.end(); + F != FEnd; ++F) { + for (unsigned I = 0, N = F->second.size(); I != N; ++I) { + OS.indent(2); + writeNodeReference(OS, F->first); + OS << " -> "; + writeNodeReference(OS, F->second[I]); + OS << ";\n"; + } + } + OS << "}\n"; +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp b/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp new file mode 100644 index 0000000..6c3bb1d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp @@ -0,0 +1,386 @@ +//===--- DiagnosticRenderer.cpp - Diagnostic Pretty-Printing --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/DiagnosticRenderer.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Lex/Lexer.h" +#include "clang/Edit/EditedSource.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditsReceiver.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/ADT/SmallString.h" +#include <algorithm> +using namespace clang; + +/// Look through spelling locations for a macro argument expansion, and +/// if found skip to it so that we can trace the argument rather than the macros +/// in which that argument is used. If no macro argument expansion is found, +/// don't skip anything and return the starting location. +static SourceLocation skipToMacroArgExpansion(const SourceManager &SM, + SourceLocation StartLoc) { + for (SourceLocation L = StartLoc; L.isMacroID(); + L = SM.getImmediateSpellingLoc(L)) { + if (SM.isMacroArgExpansion(L)) + return L; + } + + // Otherwise just return initial location, there's nothing to skip. + return StartLoc; +} + +/// Gets the location of the immediate macro caller, one level up the stack +/// toward the initial macro typed into the source. +static SourceLocation getImmediateMacroCallerLoc(const SourceManager &SM, + SourceLocation Loc) { + if (!Loc.isMacroID()) return Loc; + + // When we have the location of (part of) an expanded parameter, its spelling + // location points to the argument as typed into the macro call, and + // therefore is used to locate the macro caller. + if (SM.isMacroArgExpansion(Loc)) + return SM.getImmediateSpellingLoc(Loc); + + // Otherwise, the caller of the macro is located where this macro is + // expanded (while the spelling is part of the macro definition). + return SM.getImmediateExpansionRange(Loc).first; +} + +/// Gets the location of the immediate macro callee, one level down the stack +/// toward the leaf macro. +static SourceLocation getImmediateMacroCalleeLoc(const SourceManager &SM, + SourceLocation Loc) { + if (!Loc.isMacroID()) return Loc; + + // When we have the location of (part of) an expanded parameter, its + // expansion location points to the unexpanded paramater reference within + // the macro definition (or callee). + if (SM.isMacroArgExpansion(Loc)) + return SM.getImmediateExpansionRange(Loc).first; + + // Otherwise, the callee of the macro is located where this location was + // spelled inside the macro definition. + return SM.getImmediateSpellingLoc(Loc); +} + +/// \brief Retrieve the name of the immediate macro expansion. +/// +/// This routine starts from a source location, and finds the name of the macro +/// responsible for its immediate expansion. It looks through any intervening +/// macro argument expansions to compute this. It returns a StringRef which +/// refers to the SourceManager-owned buffer of the source where that macro +/// name is spelled. Thus, the result shouldn't out-live that SourceManager. +/// +/// This differs from Lexer::getImmediateMacroName in that any macro argument +/// location will result in the topmost function macro that accepted it. +/// e.g. +/// \code +/// MAC1( MAC2(foo) ) +/// \endcode +/// for location of 'foo' token, this function will return "MAC1" while +/// Lexer::getImmediateMacroName will return "MAC2". +static StringRef getImmediateMacroName(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + assert(Loc.isMacroID() && "Only reasonble to call this on macros"); + // Walk past macro argument expanions. + while (SM.isMacroArgExpansion(Loc)) + Loc = SM.getImmediateExpansionRange(Loc).first; + + // Find the spelling location of the start of the non-argument expansion + // range. This is where the macro name was spelled in order to begin + // expanding this macro. + Loc = SM.getSpellingLoc(SM.getImmediateExpansionRange(Loc).first); + + // Dig out the buffer where the macro name was spelled and the extents of the + // name so that we can render it into the expansion note. + std::pair<FileID, unsigned> ExpansionInfo = SM.getDecomposedLoc(Loc); + unsigned MacroTokenLength = Lexer::MeasureTokenLength(Loc, SM, LangOpts); + StringRef ExpansionBuffer = SM.getBufferData(ExpansionInfo.first); + return ExpansionBuffer.substr(ExpansionInfo.second, MacroTokenLength); +} + +/// Get the presumed location of a diagnostic message. This computes the +/// presumed location for the top of any macro backtrace when present. +static PresumedLoc getDiagnosticPresumedLoc(const SourceManager &SM, + SourceLocation Loc) { + // This is a condensed form of the algorithm used by emitCaretDiagnostic to + // walk to the top of the macro call stack. + while (Loc.isMacroID()) { + Loc = skipToMacroArgExpansion(SM, Loc); + Loc = getImmediateMacroCallerLoc(SM, Loc); + } + + return SM.getPresumedLoc(Loc); +} + +DiagnosticRenderer::DiagnosticRenderer(const SourceManager &SM, + const LangOptions &LangOpts, + const DiagnosticOptions &DiagOpts) +: SM(SM), LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {} + +DiagnosticRenderer::~DiagnosticRenderer() {} + +namespace { + +class FixitReceiver : public edit::EditsReceiver { + SmallVectorImpl<FixItHint> &MergedFixits; + +public: + FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits) + : MergedFixits(MergedFixits) { } + virtual void insert(SourceLocation loc, StringRef text) { + MergedFixits.push_back(FixItHint::CreateInsertion(loc, text)); + } + virtual void replace(CharSourceRange range, StringRef text) { + MergedFixits.push_back(FixItHint::CreateReplacement(range, text)); + } +}; + +} + +static void mergeFixits(ArrayRef<FixItHint> FixItHints, + const SourceManager &SM, const LangOptions &LangOpts, + SmallVectorImpl<FixItHint> &MergedFixits) { + edit::Commit commit(SM, LangOpts); + for (ArrayRef<FixItHint>::const_iterator + I = FixItHints.begin(), E = FixItHints.end(); I != E; ++I) { + const FixItHint &Hint = *I; + if (Hint.CodeToInsert.empty()) { + if (Hint.InsertFromRange.isValid()) + commit.insertFromRange(Hint.RemoveRange.getBegin(), + Hint.InsertFromRange, /*afterToken=*/false, + Hint.BeforePreviousInsertions); + else + commit.remove(Hint.RemoveRange); + } else { + if (Hint.RemoveRange.isTokenRange() || + Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) + commit.replace(Hint.RemoveRange, Hint.CodeToInsert); + else + commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, + /*afterToken=*/false, Hint.BeforePreviousInsertions); + } + } + + edit::EditedSource Editor(SM, LangOpts); + if (Editor.commit(commit)) { + FixitReceiver Rec(MergedFixits); + Editor.applyRewrites(Rec); + } +} + +void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<CharSourceRange> Ranges, + ArrayRef<FixItHint> FixItHints, + DiagOrStoredDiag D) { + + beginDiagnostic(D, Level); + + PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Loc); + + // First, if this diagnostic is not in the main file, print out the + // "included from" lines. + emitIncludeStack(PLoc.getIncludeLoc(), Level); + + // Next, emit the actual diagnostic message. + emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, D); + + // Only recurse if we have a valid location. + if (Loc.isValid()) { + // Get the ranges into a local array we can hack on. + SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(), + Ranges.end()); + + llvm::SmallVector<FixItHint, 8> MergedFixits; + if (!FixItHints.empty()) { + mergeFixits(FixItHints, SM, LangOpts, MergedFixits); + FixItHints = MergedFixits; + } + + for (ArrayRef<FixItHint>::const_iterator I = FixItHints.begin(), + E = FixItHints.end(); + I != E; ++I) + if (I->RemoveRange.isValid()) + MutableRanges.push_back(I->RemoveRange); + + unsigned MacroDepth = 0; + emitMacroExpansionsAndCarets(Loc, Level, MutableRanges, FixItHints, + MacroDepth); + } + + LastLoc = Loc; + LastLevel = Level; + + endDiagnostic(D, Level); +} + + +void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) { + emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(), + Diag.getRanges(), Diag.getFixIts(), + &Diag); +} + +/// \brief Prints an include stack when appropriate for a particular +/// diagnostic level and location. +/// +/// This routine handles all the logic of suppressing particular include +/// stacks (such as those for notes) and duplicate include stacks when +/// repeated warnings occur within the same file. It also handles the logic +/// of customizing the formatting and display of the include stack. +/// +/// \param Level The diagnostic level of the message this stack pertains to. +/// \param Loc The include location of the current file (not the diagnostic +/// location). +void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc, + DiagnosticsEngine::Level Level) { + // Skip redundant include stacks altogether. + if (LastIncludeLoc == Loc) + return; + LastIncludeLoc = Loc; + + if (!DiagOpts.ShowNoteIncludeStack && Level == DiagnosticsEngine::Note) + return; + + emitIncludeStackRecursively(Loc); +} + +/// \brief Helper to recursivly walk up the include stack and print each layer +/// on the way back down. +void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc) { + if (Loc.isInvalid()) + return; + + PresumedLoc PLoc = SM.getPresumedLoc(Loc); + if (PLoc.isInvalid()) + return; + + // Emit the other include frames first. + emitIncludeStackRecursively(PLoc.getIncludeLoc()); + + // Emit the inclusion text/note. + emitIncludeLocation(Loc, PLoc); +} + +/// \brief Recursively emit notes for each macro expansion and caret +/// diagnostics where appropriate. +/// +/// Walks up the macro expansion stack printing expansion notes, the code +/// snippet, caret, underlines and FixItHint display as appropriate at each +/// level. +/// +/// \param Loc The location for this caret. +/// \param Level The diagnostic level currently being emitted. +/// \param Ranges The underlined ranges for this code snippet. +/// \param Hints The FixIt hints active for this diagnostic. +/// \param MacroSkipEnd The depth to stop skipping macro expansions. +/// \param OnMacroInst The current depth of the macro expansion stack. +void DiagnosticRenderer::emitMacroExpansionsAndCarets( + SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange>& Ranges, + ArrayRef<FixItHint> Hints, + unsigned &MacroDepth, + unsigned OnMacroInst) +{ + assert(!Loc.isInvalid() && "must have a valid source location here"); + + // If this is a file source location, directly emit the source snippet and + // caret line. Also record the macro depth reached. + if (Loc.isFileID()) { + assert(MacroDepth == 0 && "We shouldn't hit a leaf node twice!"); + MacroDepth = OnMacroInst; + emitCodeContext(Loc, Level, Ranges, Hints); + return; + } + // Otherwise recurse through each macro expansion layer. + + // When processing macros, skip over the expansions leading up to + // a macro argument, and trace the argument's expansion stack instead. + Loc = skipToMacroArgExpansion(SM, Loc); + + SourceLocation OneLevelUp = getImmediateMacroCallerLoc(SM, Loc); + + // FIXME: Map ranges? + emitMacroExpansionsAndCarets(OneLevelUp, Level, Ranges, Hints, MacroDepth, + OnMacroInst + 1); + + // Save the original location so we can find the spelling of the macro call. + SourceLocation MacroLoc = Loc; + + // Map the location. + Loc = getImmediateMacroCalleeLoc(SM, Loc); + + unsigned MacroSkipStart = 0, MacroSkipEnd = 0; + if (MacroDepth > DiagOpts.MacroBacktraceLimit && + DiagOpts.MacroBacktraceLimit != 0) { + MacroSkipStart = DiagOpts.MacroBacktraceLimit / 2 + + DiagOpts.MacroBacktraceLimit % 2; + MacroSkipEnd = MacroDepth - DiagOpts.MacroBacktraceLimit / 2; + } + + // Whether to suppress printing this macro expansion. + bool Suppressed = (OnMacroInst >= MacroSkipStart && + OnMacroInst < MacroSkipEnd); + + // Map the ranges. + for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); + I != E; ++I) { + SourceLocation Start = I->getBegin(), End = I->getEnd(); + if (Start.isMacroID()) + I->setBegin(getImmediateMacroCalleeLoc(SM, Start)); + if (End.isMacroID()) + I->setEnd(getImmediateMacroCalleeLoc(SM, End)); + } + + if (Suppressed) { + // Tell the user that we've skipped contexts. + if (OnMacroInst == MacroSkipStart) { + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "(skipping " << (MacroSkipEnd - MacroSkipStart) + << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " + "see all)"; + emitBasicNote(Message.str()); + } + return; + } + + SmallString<100> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "expanded from macro '" + << getImmediateMacroName(MacroLoc, SM, LangOpts) << "'"; + emitDiagnostic(SM.getSpellingLoc(Loc), DiagnosticsEngine::Note, + Message.str(), + Ranges, ArrayRef<FixItHint>()); +} + +DiagnosticNoteRenderer::~DiagnosticNoteRenderer() {} + +void DiagnosticNoteRenderer::emitIncludeLocation(SourceLocation Loc, + PresumedLoc PLoc) { + // Generate a note indicating the include location. + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "in file included from " << PLoc.getFilename() << ':' + << PLoc.getLine() << ":"; + emitNote(Loc, Message.str()); +} + +void DiagnosticNoteRenderer::emitBasicNote(StringRef Message) { + emitNote(SourceLocation(), Message); +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp b/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp index ba2d63b..da4bdfa 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp @@ -14,14 +14,15 @@ #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/ChainedIncludesSource.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/LayoutOverrideSource.h" #include "clang/Frontend/MultiplexConsumer.h" #include "clang/Parse/ParseAST.h" #include "clang/Serialization/ASTDeserializationListener.h" #include "clang/Serialization/ASTReader.h" -#include "clang/Serialization/ChainedIncludesSource.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Timer.h" #include "llvm/Support/ErrorHandling.h" @@ -30,37 +31,70 @@ using namespace clang; namespace { -/// \brief Dumps deserialized declarations. -class DeserializedDeclsDumper : public ASTDeserializationListener { +class DelegatingDeserializationListener : public ASTDeserializationListener { ASTDeserializationListener *Previous; public: - DeserializedDeclsDumper(ASTDeserializationListener *Previous) + explicit DelegatingDeserializationListener( + ASTDeserializationListener *Previous) : Previous(Previous) { } + virtual void ReaderInitialized(ASTReader *Reader) { + if (Previous) + Previous->ReaderInitialized(Reader); + } + virtual void IdentifierRead(serialization::IdentID ID, + IdentifierInfo *II) { + if (Previous) + Previous->IdentifierRead(ID, II); + } + virtual void TypeRead(serialization::TypeIdx Idx, QualType T) { + if (Previous) + Previous->TypeRead(Idx, T); + } + virtual void DeclRead(serialization::DeclID ID, const Decl *D) { + if (Previous) + Previous->DeclRead(ID, D); + } + virtual void SelectorRead(serialization::SelectorID ID, Selector Sel) { + if (Previous) + Previous->SelectorRead(ID, Sel); + } + virtual void MacroDefinitionRead(serialization::PreprocessedEntityID PPID, + MacroDefinition *MD) { + if (Previous) + Previous->MacroDefinitionRead(PPID, MD); + } +}; + +/// \brief Dumps deserialized declarations. +class DeserializedDeclsDumper : public DelegatingDeserializationListener { +public: + explicit DeserializedDeclsDumper(ASTDeserializationListener *Previous) + : DelegatingDeserializationListener(Previous) { } + virtual void DeclRead(serialization::DeclID ID, const Decl *D) { llvm::outs() << "PCH DECL: " << D->getDeclKindName(); if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) - llvm::outs() << " - " << ND->getNameAsString(); + llvm::outs() << " - " << *ND; llvm::outs() << "\n"; - if (Previous) - Previous->DeclRead(ID, D); + DelegatingDeserializationListener::DeclRead(ID, D); } }; /// \brief Checks deserialized declarations and emits error if a name /// matches one given in command-line using -error-on-deserialized-decl. - class DeserializedDeclsChecker : public ASTDeserializationListener { + class DeserializedDeclsChecker : public DelegatingDeserializationListener { ASTContext &Ctx; std::set<std::string> NamesToCheck; - ASTDeserializationListener *Previous; public: DeserializedDeclsChecker(ASTContext &Ctx, const std::set<std::string> &NamesToCheck, ASTDeserializationListener *Previous) - : Ctx(Ctx), NamesToCheck(NamesToCheck), Previous(Previous) { } + : DelegatingDeserializationListener(Previous), + Ctx(Ctx), NamesToCheck(NamesToCheck) { } virtual void DeclRead(serialization::DeclID ID, const Decl *D) { if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) @@ -72,8 +106,7 @@ public: << ND->getNameAsString(); } - if (Previous) - Previous->DeclRead(ID, D); + DelegatingDeserializationListener::DeclRead(ID, D); } }; @@ -83,10 +116,9 @@ FrontendAction::FrontendAction() : Instance(0) {} FrontendAction::~FrontendAction() {} -void FrontendAction::setCurrentFile(StringRef Value, InputKind Kind, - ASTUnit *AST) { - CurrentFile = Value; - CurrentFileKind = Kind; +void FrontendAction::setCurrentInput(const FrontendInputFile &CurrentInput, + ASTUnit *AST) { + this->CurrentInput = CurrentInput; CurrentASTUnit.reset(AST); } @@ -112,7 +144,7 @@ ASTConsumer* FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI, ie = FrontendPluginRegistry::end(); it != ie; ++it) { if (it->getName() == CI.getFrontendOpts().AddPluginActions[i]) { - llvm::OwningPtr<PluginASTAction> P(it->instantiate()); + OwningPtr<PluginASTAction> P(it->instantiate()); FrontendAction* c = P.get(); if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i])) Consumers.push_back(c->CreateASTConsumer(CI, InFile)); @@ -124,11 +156,10 @@ ASTConsumer* FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI, } bool FrontendAction::BeginSourceFile(CompilerInstance &CI, - StringRef Filename, - InputKind InputKind) { + const FrontendInputFile &Input) { assert(!Instance && "Already processing a source file!"); - assert(!Filename.empty() && "Unexpected empty filename!"); - setCurrentFile(Filename, InputKind); + assert(!Input.File.empty() && "Unexpected empty filename!"); + setCurrentInput(Input); setCompilerInstance(&CI); if (!BeginInvocation(CI)) @@ -136,20 +167,20 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, // AST files follow a very different path, since they share objects via the // AST unit. - if (InputKind == IK_AST) { + if (Input.Kind == IK_AST) { assert(!usesPreprocessorOnly() && "Attempt to pass AST file to preprocessor only action!"); assert(hasASTFileSupport() && "This action does not have AST file support!"); - llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics()); std::string Error; - ASTUnit *AST = ASTUnit::LoadFromASTFile(Filename, Diags, + ASTUnit *AST = ASTUnit::LoadFromASTFile(Input.File, Diags, CI.getFileSystemOpts()); if (!AST) goto failure; - setCurrentFile(Filename, InputKind, AST); + setCurrentInput(Input, AST); // Set the shared objects, these are reset when we finish processing the // file, otherwise the CompilerInstance will happily destroy them. @@ -159,11 +190,11 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.setASTContext(&AST->getASTContext()); // Initialize the action. - if (!BeginSourceFileAction(CI, Filename)) + if (!BeginSourceFileAction(CI, Input.File)) goto failure; /// Create the AST consumer. - CI.setASTConsumer(CreateWrappedASTConsumer(CI, Filename)); + CI.setASTConsumer(CreateWrappedASTConsumer(CI, Input.File)); if (!CI.hasASTConsumer()) goto failure; @@ -177,7 +208,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.createSourceManager(CI.getFileManager()); // IR files bypass the rest of initialization. - if (InputKind == IK_LLVM_IR) { + if (Input.Kind == IK_LLVM_IR) { assert(hasIRSupport() && "This action does not have IR file support!"); @@ -185,7 +216,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 0); // Initialize the action. - if (!BeginSourceFileAction(CI, Filename)) + if (!BeginSourceFileAction(CI, Input.File)) goto failure; return true; @@ -199,7 +230,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, &CI.getPreprocessor()); // Initialize the action. - if (!BeginSourceFileAction(CI, Filename)) + if (!BeginSourceFileAction(CI, Input.File)) goto failure; /// Create the AST context and consumer unless this is a preprocessor only @@ -207,8 +238,8 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, if (!usesPreprocessorOnly()) { CI.createASTContext(); - llvm::OwningPtr<ASTConsumer> Consumer( - CreateWrappedASTConsumer(CI, Filename)); + OwningPtr<ASTConsumer> Consumer( + CreateWrappedASTConsumer(CI, Input.File)); if (!Consumer) goto failure; @@ -216,7 +247,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) { // Convert headers to PCH and chain them. - llvm::OwningPtr<ExternalASTSource> source; + OwningPtr<ExternalASTSource> source; source.reset(ChainedIncludesSource::create(CI)); if (!source) goto failure; @@ -237,6 +268,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.getPreprocessorOpts().ImplicitPCHInclude, CI.getPreprocessorOpts().DisablePCHValidation, CI.getPreprocessorOpts().DisableStatCache, + CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, DeserialListener); if (!CI.getASTContext().getExternalSource()) goto failure; @@ -252,9 +284,19 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, if (!CI.hasASTContext() || !CI.getASTContext().getExternalSource()) { Preprocessor &PP = CI.getPreprocessor(); PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(), - PP.getLangOptions()); + PP.getLangOpts()); } + // If there is a layout overrides file, attach an external AST source that + // provides the layouts from that file. + if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() && + CI.hasASTContext() && !CI.getASTContext().getExternalSource()) { + OwningPtr<ExternalASTSource> + Override(new LayoutOverrideSource( + CI.getFrontendOpts().OverrideRecordLayoutsFile)); + CI.getASTContext().setExternalSource(Override); + } + return true; // If we failed, reset state since the client will not end up calling the @@ -268,7 +310,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, } CI.getDiagnosticClient().EndSourceFile(); - setCurrentFile("", IK_None); + setCurrentInput(FrontendInputFile()); setCompilerInstance(0); return false; } @@ -278,17 +320,11 @@ void FrontendAction::Execute() { // Initialize the main file entry. This needs to be delayed until after PCH // has loaded. - if (isCurrentFileAST()) { - // Set the main file ID to an empty file. - // - // FIXME: We probably shouldn't need this, but for now this is the - // simplest way to reuse the logic in ParseAST. - const char *EmptyStr = ""; - llvm::MemoryBuffer *SB = - llvm::MemoryBuffer::getMemBuffer(EmptyStr, "<dummy input>"); - CI.getSourceManager().createMainFileIDForMemBuffer(SB); - } else { - if (!CI.InitializeSourceManager(getCurrentFile())) + if (!isCurrentFileAST()) { + if (!CI.InitializeSourceManager(getCurrentFile(), + getCurrentInput().IsSystem + ? SrcMgr::C_System + : SrcMgr::C_User)) return; } @@ -352,7 +388,7 @@ void FrontendAction::EndSourceFile() { } setCompilerInstance(0); - setCurrentFile("", IK_None); + setCurrentInput(FrontendInputFile()); } //===----------------------------------------------------------------------===// @@ -376,9 +412,12 @@ void ASTFrontendAction::ExecuteAction() { if (!CI.hasSema()) CI.createSema(getTranslationUnitKind(), CompletionConsumer); - ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats); + ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats, + CI.getFrontendOpts().SkipFunctionBodies); } +void PluginASTAction::anchor() { } + ASTConsumer * PreprocessorFrontendAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { @@ -394,7 +433,7 @@ bool WrapperFrontendAction::BeginInvocation(CompilerInstance &CI) { } bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) { - WrappedAction->setCurrentFile(getCurrentFile(), getCurrentFileKind()); + WrappedAction->setCurrentInput(getCurrentInput()); WrappedAction->setCompilerInstance(&CI); return WrappedAction->BeginSourceFileAction(CI, Filename); } diff --git a/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp b/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp index 6f84da9..b4a439d 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp @@ -9,6 +9,7 @@ #include "clang/Frontend/FrontendActions.h" #include "clang/AST/ASTConsumer.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Pragma.h" #include "clang/Lex/Preprocessor.h" #include "clang/Parse/Parser.h" @@ -20,9 +21,11 @@ #include "clang/Frontend/Utils.h" #include "clang/Serialization/ASTWriter.h" #include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/system_error.h" +#include <set> using namespace clang; @@ -85,8 +88,7 @@ ASTConsumer *GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, if (!CI.getFrontendOpts().RelocatablePCH) Sysroot.clear(); - return new PCHGenerator(CI.getPreprocessor(), OutputFile, MakeModule, - Sysroot, OS); + return new PCHGenerator(CI.getPreprocessor(), OutputFile, 0, Sysroot, OS); } bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI, @@ -113,11 +115,317 @@ bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI, return false; } +ASTConsumer *GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::string Sysroot; + std::string OutputFile; + raw_ostream *OS = 0; + if (ComputeASTConsumerArguments(CI, InFile, Sysroot, OutputFile, OS)) + return 0; + + return new PCHGenerator(CI.getPreprocessor(), OutputFile, Module, + Sysroot, OS); +} + +/// \brief Collect the set of header includes needed to construct the given +/// module. +/// +/// \param Module The module we're collecting includes from. +/// +/// \param Includes Will be augmented with the set of #includes or #imports +/// needed to load all of the named headers. +static void collectModuleHeaderIncludes(const LangOptions &LangOpts, + FileManager &FileMgr, + ModuleMap &ModMap, + clang::Module *Module, + SmallString<256> &Includes) { + // Don't collect any headers for unavailable modules. + if (!Module->isAvailable()) + return; + + // Add includes for each of these headers. + for (unsigned I = 0, N = Module->Headers.size(); I != N; ++I) { + if (LangOpts.ObjC1) + Includes += "#import \""; + else + Includes += "#include \""; + Includes += Module->Headers[I]->getName(); + Includes += "\"\n"; + } + + if (const FileEntry *UmbrellaHeader = Module->getUmbrellaHeader()) { + if (Module->Parent) { + // Include the umbrella header for submodules. + if (LangOpts.ObjC1) + Includes += "#import \""; + else + Includes += "#include \""; + Includes += UmbrellaHeader->getName(); + Includes += "\"\n"; + } + } else if (const DirectoryEntry *UmbrellaDir = Module->getUmbrellaDir()) { + // Add all of the headers we find in this subdirectory. + llvm::error_code EC; + SmallString<128> DirNative; + llvm::sys::path::native(UmbrellaDir->getName(), DirNative); + for (llvm::sys::fs::recursive_directory_iterator Dir(DirNative.str(), EC), + DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + // Check whether this entry has an extension typically associated with + // headers. + if (!llvm::StringSwitch<bool>(llvm::sys::path::extension(Dir->path())) + .Cases(".h", ".H", ".hh", ".hpp", true) + .Default(false)) + continue; + + // If this header is marked 'unavailable' in this module, don't include + // it. + if (const FileEntry *Header = FileMgr.getFile(Dir->path())) + if (ModMap.isHeaderInUnavailableModule(Header)) + continue; + + // Include this header umbrella header for submodules. + if (LangOpts.ObjC1) + Includes += "#import \""; + else + Includes += "#include \""; + Includes += Dir->path(); + Includes += "\"\n"; + } + } + + // Recurse into submodules. + for (clang::Module::submodule_iterator Sub = Module->submodule_begin(), + SubEnd = Module->submodule_end(); + Sub != SubEnd; ++Sub) + collectModuleHeaderIncludes(LangOpts, FileMgr, ModMap, *Sub, Includes); +} + +bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + // Find the module map file. + const FileEntry *ModuleMap = CI.getFileManager().getFile(Filename); + if (!ModuleMap) { + CI.getDiagnostics().Report(diag::err_module_map_not_found) + << Filename; + return false; + } + + // Parse the module map file. + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + if (HS.loadModuleMapFile(ModuleMap)) + return false; + + if (CI.getLangOpts().CurrentModule.empty()) { + CI.getDiagnostics().Report(diag::err_missing_module_name); + + // FIXME: Eventually, we could consider asking whether there was just + // a single module described in the module map, and use that as a + // default. Then it would be fairly trivial to just "compile" a module + // map with a single module (the common case). + return false; + } + + // Dig out the module definition. + Module = HS.lookupModule(CI.getLangOpts().CurrentModule, + /*AllowSearch=*/false); + if (!Module) { + CI.getDiagnostics().Report(diag::err_missing_module) + << CI.getLangOpts().CurrentModule << Filename; + + return false; + } + + // Check whether we can build this module at all. + StringRef Feature; + if (!Module->isAvailable(CI.getLangOpts(), CI.getTarget(), Feature)) { + CI.getDiagnostics().Report(diag::err_module_unavailable) + << Module->getFullModuleName() + << Feature; + + return false; + } + + // Do we have an umbrella header for this module? + const FileEntry *UmbrellaHeader = Module->getUmbrellaHeader(); + + // Collect the set of #includes we need to build the module. + SmallString<256> HeaderContents; + collectModuleHeaderIncludes(CI.getLangOpts(), CI.getFileManager(), + CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), + Module, HeaderContents); + if (UmbrellaHeader && HeaderContents.empty()) { + // Simple case: we have an umbrella header and there are no additional + // includes, we can just parse the umbrella header directly. + setCurrentInput(FrontendInputFile(UmbrellaHeader->getName(), + getCurrentFileKind(), + Module->IsSystem)); + return true; + } + + FileManager &FileMgr = CI.getFileManager(); + SmallString<128> HeaderName; + time_t ModTime; + if (UmbrellaHeader) { + // Read in the umbrella header. + // FIXME: Go through the source manager; the umbrella header may have + // been overridden. + std::string ErrorStr; + llvm::MemoryBuffer *UmbrellaContents + = FileMgr.getBufferForFile(UmbrellaHeader, &ErrorStr); + if (!UmbrellaContents) { + CI.getDiagnostics().Report(diag::err_missing_umbrella_header) + << UmbrellaHeader->getName() << ErrorStr; + return false; + } + + // Combine the contents of the umbrella header with the automatically- + // generated includes. + SmallString<256> OldContents = HeaderContents; + HeaderContents = UmbrellaContents->getBuffer(); + HeaderContents += "\n\n"; + HeaderContents += "/* Module includes */\n"; + HeaderContents += OldContents; + + // Pretend that we're parsing the umbrella header. + HeaderName = UmbrellaHeader->getName(); + ModTime = UmbrellaHeader->getModificationTime(); + + delete UmbrellaContents; + } else { + // Pick an innocuous-sounding name for the umbrella header. + HeaderName = Module->Name + ".h"; + if (FileMgr.getFile(HeaderName, /*OpenFile=*/false, + /*CacheFailure=*/false)) { + // Try again! + HeaderName = Module->Name + "-module.h"; + if (FileMgr.getFile(HeaderName, /*OpenFile=*/false, + /*CacheFailure=*/false)) { + // Pick something ridiculous and go with it. + HeaderName = Module->Name + "-module.hmod"; + } + } + ModTime = time(0); + } + + // Remap the contents of the header name we're using to our synthesized + // buffer. + const FileEntry *HeaderFile = FileMgr.getVirtualFile(HeaderName, + HeaderContents.size(), + ModTime); + llvm::MemoryBuffer *HeaderContentsBuf + = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents); + CI.getSourceManager().overrideFileContents(HeaderFile, HeaderContentsBuf); + setCurrentInput(FrontendInputFile(HeaderName, getCurrentFileKind(), + Module->IsSystem)); + return true; +} + +bool GenerateModuleAction::ComputeASTConsumerArguments(CompilerInstance &CI, + StringRef InFile, + std::string &Sysroot, + std::string &OutputFile, + raw_ostream *&OS) { + // If no output file was provided, figure out where this module would go + // in the module cache. + if (CI.getFrontendOpts().OutputFile.empty()) { + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + SmallString<256> ModuleFileName(HS.getModuleCachePath()); + llvm::sys::path::append(ModuleFileName, + CI.getLangOpts().CurrentModule + ".pcm"); + CI.getFrontendOpts().OutputFile = ModuleFileName.str(); + } + + // We use createOutputFile here because this is exposed via libclang, and we + // must disable the RemoveFileOnSignal behavior. + // We use a temporary to avoid race conditions. + OS = CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true, + /*RemoveFileOnSignal=*/false, InFile, + /*Extension=*/"", /*useTemporary=*/true, + /*CreateMissingDirectories=*/true); + if (!OS) + return true; + + OutputFile = CI.getFrontendOpts().OutputFile; + return false; +} + ASTConsumer *SyntaxOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { return new ASTConsumer(); } +namespace { + class PubnamesDumpConsumer : public ASTConsumer { + Preprocessor &PP; + + /// \brief Determine whether the given identifier provides a 'public' name. + bool isPublicName(IdentifierInfo *II) { + // If there are any top-level declarations associated with this + // identifier, it is a public name. + if (II->getFETokenInfo<void>()) + return true; + + // If this identifier is the name of a non-builtin macro that isn't + // defined on the command line or implicitly by the front end, it is a + // public name. + if (II->hasMacroDefinition()) { + if (MacroInfo *M = PP.getMacroInfo(II)) + if (!M->isBuiltinMacro()) { + SourceLocation Loc = M->getDefinitionLoc(); + FileID File = PP.getSourceManager().getFileID(Loc); + if (PP.getSourceManager().getFileEntryForID(File)) + return true; + } + } + + return false; + } + + public: + PubnamesDumpConsumer(Preprocessor &PP) : PP(PP) { } + + virtual void HandleTranslationUnit(ASTContext &Ctx) { + std::set<StringRef> Pubnames; + + // Add the names of any non-builtin macros. + for (IdentifierTable::iterator I = Ctx.Idents.begin(), + IEnd = Ctx.Idents.end(); + I != IEnd; ++I) { + if (isPublicName(I->second)) + Pubnames.insert(I->first()); + } + + // If there is an external identifier lookup source, consider those + // identifiers as well. + if (IdentifierInfoLookup *External + = Ctx.Idents.getExternalIdentifierLookup()) { + OwningPtr<IdentifierIterator> Iter(External->getIdentifiers()); + do { + StringRef Name = Iter->Next(); + if (Name.empty()) + break; + + if (isPublicName(PP.getIdentifierInfo(Name))) + Pubnames.insert(Name); + } while (true); + } + + // Print the names, in lexicographical order. + for (std::set<StringRef>::iterator N = Pubnames.begin(), + NEnd = Pubnames.end(); + N != NEnd; ++N) { + llvm::outs() << *N << '\n'; + } + } + }; +} + +ASTConsumer *PubnamesDumpAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return new PubnamesDumpConsumer(CI.getPreprocessor()); +} + //===----------------------------------------------------------------------===// // Preprocessor Actions //===----------------------------------------------------------------------===// @@ -128,7 +436,7 @@ void DumpRawTokensAction::ExecuteAction() { // Start lexing the specified input file. const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID()); - Lexer RawLex(SM.getMainFileID(), FromFile, SM, PP.getLangOptions()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, PP.getLangOpts()); RawLex.SetKeepWhitespaceMode(true); Token RawTok; diff --git a/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp b/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp index 3b41d89..79920df 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp @@ -11,6 +11,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -107,10 +108,10 @@ void HeaderIncludesCallback::FileChanged(SourceLocation Loc, // are showing all headers. if (ShowHeader && Reason == PPCallbacks::EnterFile) { // Write to a temporary string to avoid unnecessary flushing on errs(). - llvm::SmallString<512> Filename(UserLoc.getFilename()); + SmallString<512> Filename(UserLoc.getFilename()); Lexer::Stringify(Filename); - llvm::SmallString<256> Msg; + SmallString<256> Msg; if (ShowDepth) { // The main source file is at depth 1, so skip one dot. for (unsigned i = 1; i != CurrentIncludeDepth; ++i) diff --git a/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp b/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp index 1b1c551..7f01cd9 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp @@ -11,10 +11,6 @@ // //===----------------------------------------------------------------------===// -#ifdef HAVE_CLANG_CONFIG_H -# include "clang/Config/config.h" -#endif - #include "clang/Frontend/Utils.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.h" @@ -30,7 +26,9 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Path.h" -#include "llvm/Config/config.h" + +#include "clang/Config/config.h" // C_INCLUDE_DIRS + #ifndef CLANG_PREFIX #define CLANG_PREFIX #endif @@ -112,7 +110,7 @@ void InitHeaderSearch::AddPath(const Twine &Path, FileManager &FM = Headers.getFileMgr(); // Compute the actual path, taking into consideration -isysroot. - llvm::SmallString<256> MappedPathStorage; + SmallString<256> MappedPathStorage; StringRef MappedPathStr = Path.toStringRef(MappedPathStorage); // Handle isysroot. @@ -336,19 +334,6 @@ void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple, void InitHeaderSearch:: AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple, const HeaderSearchOptions &HSOpts) { llvm::Triple::OSType os = triple.getOS(); - StringRef CxxIncludeRoot(CXX_INCLUDE_ROOT); - if (CxxIncludeRoot != "") { - StringRef CxxIncludeArch(CXX_INCLUDE_ARCH); - if (CxxIncludeArch == "") - AddGnuCPlusPlusIncludePaths(CxxIncludeRoot, triple.str().c_str(), - CXX_INCLUDE_32BIT_DIR, CXX_INCLUDE_64BIT_DIR, - triple); - else - AddGnuCPlusPlusIncludePaths(CxxIncludeRoot, CXX_INCLUDE_ARCH, - CXX_INCLUDE_32BIT_DIR, CXX_INCLUDE_64BIT_DIR, - triple); - return; - } // FIXME: temporary hack: hard-coded paths. if (triple.isOSDarwin()) { @@ -391,11 +376,10 @@ AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple, const HeaderSearchOp case llvm::Triple::Cygwin: // Cygwin-1.7 + AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.5.3"); AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.4"); // g++-4 / Cygwin-1.5 AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.2"); - // FIXME: Do we support g++-3.4.4? - AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "3.4.4"); break; case llvm::Triple::MinGW32: // mingw-w64 C++ include paths (i686-w64-mingw32 and x86_64-w64-mingw32) @@ -403,12 +387,17 @@ AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple, const HeaderSearchOp AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.1"); AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.2"); AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.3"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.4"); AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.0"); AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.1"); AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.2"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.3"); AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.7.0"); // mingw.org C++ include paths AddMinGWCPlusPlusIncludePaths("/mingw/lib/gcc", "mingw32", "4.5.2"); //MSYS + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.6.2"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.6.1"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.5.2"); AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.5.0"); AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.4.0"); AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.3.0"); @@ -440,6 +429,8 @@ AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple, const HeaderSearchOp "", "", "", triple); break; case llvm::Triple::Solaris: + AddGnuCPlusPlusIncludePaths("/usr/gcc/4.5/include/c++/4.5.2/", + "i386-pc-solaris2.11", "", "", triple); // Solaris - Fall though.. case llvm::Triple::AuroraUX: // AuroraUX @@ -484,6 +475,11 @@ void InitHeaderSearch::AddDefaultIncludePaths(const LangOptions &Lang, AddPath(P.str(), CXXSystem, true, false, false, true); } } + // On Solaris, include the support directory for things like xlocale and + // fudged system headers. + if (triple.getOS() == llvm::Triple::Solaris) + AddPath("/usr/include/c++/v1/support/solaris", CXXSystem, true, false, + false); AddPath("/usr/include/c++/v1", CXXSystem, true, false, false); } else { @@ -675,5 +671,13 @@ void clang::ApplyHeaderSearchOptions(HeaderSearch &HS, Init.AddDefaultIncludePaths(Lang, Triple, HSOpts); + if (HSOpts.UseBuiltinIncludes) { + // Set up the builtin include directory in the module map. + llvm::sys::Path P(HSOpts.ResourceDir); + P.appendComponent("include"); + if (const DirectoryEntry *Dir = HS.getFileMgr().getDirectory(P.str())) + HS.getModuleMap().setBuiltinIncludeDir(Dir); + } + Init.Realize(Lang); } diff --git a/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp b/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp index 6f49ec4..93d49b0 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp @@ -18,6 +18,7 @@ #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Frontend/PreprocessorOptions.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" @@ -48,39 +49,19 @@ static void DefineBuiltinMacro(MacroBuilder &Builder, StringRef Macro, } } -std::string clang::NormalizeDashIncludePath(StringRef File, - FileManager &FileMgr) { - // Implicit include paths should be resolved relative to the current - // working directory first, and then use the regular header search - // mechanism. The proper way to handle this is to have the - // predefines buffer located at the current working directory, but - // it has no file entry. For now, workaround this by using an - // absolute path if we find the file here, and otherwise letting - // header search handle it. - llvm::SmallString<128> Path(File); - llvm::sys::fs::make_absolute(Path); - bool exists; - if (llvm::sys::fs::exists(Path.str(), exists) || !exists) - Path = File; - else if (exists) - FileMgr.getFile(File); - - return Lexer::Stringify(Path.str()); -} - /// AddImplicitInclude - Add an implicit #include of the specified file to the /// predefines buffer. static void AddImplicitInclude(MacroBuilder &Builder, StringRef File, FileManager &FileMgr) { - Builder.append("#include \"" + - Twine(NormalizeDashIncludePath(File, FileMgr)) + "\""); + Builder.append(Twine("#include \"") + + HeaderSearch::NormalizeDashIncludePath(File, FileMgr) + "\""); } static void AddImplicitIncludeMacros(MacroBuilder &Builder, StringRef File, FileManager &FileMgr) { - Builder.append("#__include_macros \"" + - Twine(NormalizeDashIncludePath(File, FileMgr)) + "\""); + Builder.append(Twine("#__include_macros \"") + + HeaderSearch::NormalizeDashIncludePath(File, FileMgr) + "\""); // Marker token to stop the __include_macros fetch loop. Builder.append("##"); // ##? } @@ -146,7 +127,7 @@ static void DefineFloatMacros(MacroBuilder &Builder, StringRef Prefix, "1.79769313486231580793728971405301e+308L", "1.18973149535723176508575932662800702e+4932L"); - llvm::SmallString<32> DefPrefix; + SmallString<32> DefPrefix; DefPrefix = "__"; DefPrefix += Prefix; DefPrefix += "_"; @@ -221,6 +202,20 @@ static void DefineExactWidthIntType(TargetInfo::IntType Ty, ConstSuffix); } +/// Get the value the ATOMIC_*_LOCK_FREE macro should have for a type with +/// the specified properties. +static const char *getLockFreeValue(unsigned TypeWidth, unsigned TypeAlign, + unsigned InlineWidth) { + // Fully-aligned, power-of-2 sizes no larger than the inline + // width will be inlined as lock-free operations. + if (TypeWidth == TypeAlign && (TypeWidth & (TypeWidth - 1)) == 0 && + TypeWidth <= InlineWidth) + return "2"; // "always lock free" + // We cannot be certain what operations the lib calls might be + // able to implement as lock-free on future processors. + return "1"; // "sometimes lock free" +} + /// \brief Add definitions required for a smooth interaction between /// Objective-C++ automated reference counting and libstdc++ (4.2). static void AddObjCXXARCLibstdcxxDefines(const LangOptions &LangOpts, @@ -278,7 +273,7 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI, const LangOptions &LangOpts, const FrontendOptions &FEOpts, MacroBuilder &Builder) { - if (!LangOpts.MicrosoftExt && !LangOpts.TraditionalCPP) + if (!LangOpts.MicrosoftMode && !LangOpts.TraditionalCPP) Builder.defineMacro("__STDC__"); if (LangOpts.Freestanding) Builder.defineMacro("__STDC_HOSTED__", "0"); @@ -286,7 +281,9 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI, Builder.defineMacro("__STDC_HOSTED__"); if (!LangOpts.CPlusPlus) { - if (LangOpts.C99) + if (LangOpts.C11) + Builder.defineMacro("__STDC_VERSION__", "201112L"); + else if (LangOpts.C99) Builder.defineMacro("__STDC_VERSION__", "199901L"); else if (!LangOpts.GNUMode && LangOpts.Digraphs) Builder.defineMacro("__STDC_VERSION__", "199409L"); @@ -336,11 +333,25 @@ static void InitializePredefinedMacros(const TargetInfo &TI, + getClangFullRepositoryVersion() + ")\""); #undef TOSTR #undef TOSTR2 - // Currently claim to be compatible with GCC 4.2.1-5621. - Builder.defineMacro("__GNUC_MINOR__", "2"); - Builder.defineMacro("__GNUC_PATCHLEVEL__", "1"); - Builder.defineMacro("__GNUC__", "4"); - Builder.defineMacro("__GXX_ABI_VERSION", "1002"); + if (!LangOpts.MicrosoftMode) { + // Currently claim to be compatible with GCC 4.2.1-5621, but only if we're + // not compiling for MSVC compatibility + Builder.defineMacro("__GNUC_MINOR__", "2"); + Builder.defineMacro("__GNUC_PATCHLEVEL__", "1"); + Builder.defineMacro("__GNUC__", "4"); + Builder.defineMacro("__GXX_ABI_VERSION", "1002"); + } + + // Define macros for the C11 / C++11 memory orderings + Builder.defineMacro("__ATOMIC_RELAXED", "0"); + Builder.defineMacro("__ATOMIC_CONSUME", "1"); + Builder.defineMacro("__ATOMIC_ACQUIRE", "2"); + Builder.defineMacro("__ATOMIC_RELEASE", "3"); + Builder.defineMacro("__ATOMIC_ACQ_REL", "4"); + Builder.defineMacro("__ATOMIC_SEQ_CST", "5"); + + // Support for #pragma redefine_extname (Sun compatibility) + Builder.defineMacro("__PRAGMA_REDEFINE_EXTNAME", "1"); // As sad as it is, enough software depends on the __VERSION__ for version // checks that it is necessary to report 4.2.1 (the base GCC version we claim @@ -428,6 +439,9 @@ static void InitializePredefinedMacros(const TargetInfo &TI, if (LangOpts.OptimizeSize) Builder.defineMacro("__OPTIMIZE_SIZE__"); + if (LangOpts.FastMath) + Builder.defineMacro("__FAST_MATH__"); + // Initialize target-specific preprocessor defines. // Define type sizing macros based on the target properties. @@ -521,16 +535,46 @@ static void InitializePredefinedMacros(const TargetInfo &TI, else Builder.defineMacro("__GNUC_STDC_INLINE__"); - if (LangOpts.NoInline) + // The value written by __atomic_test_and_set. + // FIXME: This is target-dependent. + Builder.defineMacro("__GCC_ATOMIC_TEST_AND_SET_TRUEVAL", "1"); + + // Used by libstdc++ to implement ATOMIC_<foo>_LOCK_FREE. + unsigned InlineWidthBits = TI.getMaxAtomicInlineWidth(); +#define DEFINE_LOCK_FREE_MACRO(TYPE, Type) \ + Builder.defineMacro("__GCC_ATOMIC_" #TYPE "_LOCK_FREE", \ + getLockFreeValue(TI.get##Type##Width(), \ + TI.get##Type##Align(), \ + InlineWidthBits)); + DEFINE_LOCK_FREE_MACRO(BOOL, Bool); + DEFINE_LOCK_FREE_MACRO(CHAR, Char); + DEFINE_LOCK_FREE_MACRO(CHAR16_T, Char16); + DEFINE_LOCK_FREE_MACRO(CHAR32_T, Char32); + DEFINE_LOCK_FREE_MACRO(WCHAR_T, WChar); + DEFINE_LOCK_FREE_MACRO(SHORT, Short); + DEFINE_LOCK_FREE_MACRO(INT, Int); + DEFINE_LOCK_FREE_MACRO(LONG, Long); + DEFINE_LOCK_FREE_MACRO(LLONG, LongLong); + Builder.defineMacro("__GCC_ATOMIC_POINTER_LOCK_FREE", + getLockFreeValue(TI.getPointerWidth(0), + TI.getPointerAlign(0), + InlineWidthBits)); +#undef DEFINE_LOCK_FREE_MACRO + + if (LangOpts.NoInlineDefine) Builder.defineMacro("__NO_INLINE__"); if (unsigned PICLevel = LangOpts.PICLevel) { Builder.defineMacro("__PIC__", Twine(PICLevel)); Builder.defineMacro("__pic__", Twine(PICLevel)); } + if (unsigned PIELevel = LangOpts.PIELevel) { + Builder.defineMacro("__PIE__", Twine(PIELevel)); + Builder.defineMacro("__pie__", Twine(PIELevel)); + } // Macros to control C99 numerics and <float.h> - Builder.defineMacro("__FLT_EVAL_METHOD__", "0"); + Builder.defineMacro("__FLT_EVAL_METHOD__", Twine(TI.getFloatEvalMethod())); Builder.defineMacro("__FLT_RADIX__", "2"); int Dig = PickFP(&TI.getLongDoubleFormat(), -1/*FIXME*/, 17, 21, 33, 36); Builder.defineMacro("__DECIMAL_DIG__", Twine(Dig)); @@ -632,7 +676,7 @@ void clang::InitializePreprocessor(Preprocessor &PP, const PreprocessorOptions &InitOpts, const HeaderSearchOptions &HSOpts, const FrontendOptions &FEOpts) { - const LangOptions &LangOpts = PP.getLangOptions(); + const LangOptions &LangOpts = PP.getLangOpts(); std::string PredefineBuffer; PredefineBuffer.reserve(4080); llvm::raw_string_ostream Predefines(PredefineBuffer); @@ -641,14 +685,10 @@ void clang::InitializePreprocessor(Preprocessor &PP, InitializeFileRemapping(PP.getDiagnostics(), PP.getSourceManager(), PP.getFileManager(), InitOpts); - // Specify whether the preprocessor should replace #include/#import with - // module imports when plausible. - PP.setAutoModuleImport(InitOpts.AutoModuleImport); - // Emit line markers for various builtin sections of the file. We don't do // this in asm preprocessor mode, because "# 4" is not a line marker directive // in this mode. - if (!PP.getLangOptions().AsmPreprocessor) + if (!PP.getLangOpts().AsmPreprocessor) Builder.append("# 1 \"<built-in>\" 3"); // Install things like __POWERPC__, __GNUC__, etc into the macro table. @@ -673,12 +713,12 @@ void clang::InitializePreprocessor(Preprocessor &PP, // Even with predefines off, some macros are still predefined. // These should all be defined in the preprocessor according to the // current language configuration. - InitializeStandardPredefinedMacros(PP.getTargetInfo(), PP.getLangOptions(), + InitializeStandardPredefinedMacros(PP.getTargetInfo(), PP.getLangOpts(), FEOpts, Builder); // Add on the predefines from the driver. Wrap in a #line directive to report // that they come from the command line. - if (!PP.getLangOptions().AsmPreprocessor) + if (!PP.getLangOpts().AsmPreprocessor) Builder.append("# 1 \"<command line>\" 1"); // Process #define's and #undef's in the order they are given. @@ -706,7 +746,7 @@ void clang::InitializePreprocessor(Preprocessor &PP, } // Exit the command line and go back to <built-in> (2 is LC_LEAVE). - if (!PP.getLangOptions().AsmPreprocessor) + if (!PP.getLangOpts().AsmPreprocessor) Builder.append("# 1 \"<built-in>\" 2"); // Instruct the preprocessor to skip the preamble. @@ -718,6 +758,6 @@ void clang::InitializePreprocessor(Preprocessor &PP, // Initialize the header search object. ApplyHeaderSearchOptions(PP.getHeaderSearchInfo(), HSOpts, - PP.getLangOptions(), + PP.getLangOpts(), PP.getTargetInfo().getTriple()); } diff --git a/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp b/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp index abb521b..f86a574 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp @@ -19,14 +19,13 @@ using namespace clang::frontend; const LangStandard &LangStandard::getLangStandardForKind(Kind K) { switch (K) { - default: - llvm_unreachable("Invalid language kind!"); case lang_unspecified: llvm::report_fatal_error("getLangStandardForKind() on unspecified kind"); #define LANGSTANDARD(id, name, desc, features) \ case lang_##id: return Lang_##id; #include "clang/Frontend/LangStandards.def" } + llvm_unreachable("Invalid language kind!"); } const LangStandard *LangStandard::getLangStandardForName(StringRef Name) { diff --git a/contrib/llvm/tools/clang/lib/Frontend/LayoutOverrideSource.cpp b/contrib/llvm/tools/clang/lib/Frontend/LayoutOverrideSource.cpp new file mode 100644 index 0000000..eb7865e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/LayoutOverrideSource.cpp @@ -0,0 +1,206 @@ +//===--- LayoutOverrideSource.cpp --Override Record Layouts ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/Frontend/LayoutOverrideSource.h" +#include "clang/AST/Decl.h" +#include "llvm/Support/raw_ostream.h" +#include <fstream> +#include <string> + +using namespace clang; + +/// \brief Parse a simple identifier. +static std::string parseName(StringRef S) { + unsigned Offset = 0; + while (Offset < S.size() && + (isalpha(S[Offset]) || S[Offset] == '_' || + (Offset > 0 && isdigit(S[Offset])))) + ++Offset; + + return S.substr(0, Offset).str(); +} + +LayoutOverrideSource::LayoutOverrideSource(llvm::StringRef Filename) { + std::ifstream Input(Filename.str().c_str()); + if (!Input.is_open()) + return; + + // Parse the output of -fdump-record-layouts. + std::string CurrentType; + Layout CurrentLayout; + bool ExpectingType = false; + + while (Input.good()) { + std::string Line; + getline(Input, Line); + + StringRef LineStr(Line); + + // Determine whether the following line will start a + if (LineStr.find("*** Dumping AST Record Layout") != StringRef::npos) { + // Flush the last type/layout, if there is one. + if (!CurrentType.empty()) + Layouts[CurrentType] = CurrentLayout; + CurrentLayout = Layout(); + + ExpectingType = true; + continue; + } + + // If we're expecting a type, grab it. + if (ExpectingType) { + ExpectingType = false; + + StringRef::size_type Pos; + if ((Pos = LineStr.find("struct ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("struct ")); + else if ((Pos = LineStr.find("class ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("class ")); + else if ((Pos = LineStr.find("union ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("union ")); + else + continue; + + // Find the name of the type. + CurrentType = parseName(LineStr); + CurrentLayout = Layout(); + continue; + } + + // Check for the size of the type. + StringRef::size_type Pos = LineStr.find(" Size:"); + if (Pos != StringRef::npos) { + // Skip past the " Size:" prefix. + LineStr = LineStr.substr(Pos + strlen(" Size:")); + + unsigned long long Size = 0; + (void)LineStr.getAsInteger(10, Size); + CurrentLayout.Size = Size; + continue; + } + + // Check for the alignment of the type. + Pos = LineStr.find("Alignment:"); + if (Pos != StringRef::npos) { + // Skip past the "Alignment:" prefix. + LineStr = LineStr.substr(Pos + strlen("Alignment:")); + + unsigned long long Alignment = 0; + (void)LineStr.getAsInteger(10, Alignment); + CurrentLayout.Align = Alignment; + continue; + } + + // Check for the size/alignment of the type. + Pos = LineStr.find("sizeof="); + if (Pos != StringRef::npos) { + /* Skip past the sizeof= prefix. */ + LineStr = LineStr.substr(Pos + strlen("sizeof=")); + + // Parse size. + unsigned long long Size = 0; + (void)LineStr.getAsInteger(10, Size); + CurrentLayout.Size = Size; + + Pos = LineStr.find("align="); + if (Pos != StringRef::npos) { + /* Skip past the align= prefix. */ + LineStr = LineStr.substr(Pos + strlen("align=")); + + // Parse alignment. + unsigned long long Alignment = 0; + (void)LineStr.getAsInteger(10, Alignment); + CurrentLayout.Align = Alignment; + } + + continue; + } + + // Check for the field offsets of the type. + Pos = LineStr.find("FieldOffsets: ["); + if (Pos == StringRef::npos) + continue; + + LineStr = LineStr.substr(Pos + strlen("FieldOffsets: [")); + while (!LineStr.empty() && isdigit(LineStr[0])) { + // Parse this offset. + unsigned Idx = 1; + while (Idx < LineStr.size() && isdigit(LineStr[Idx])) + ++Idx; + + unsigned long long Offset = 0; + (void)LineStr.substr(0, Idx).getAsInteger(10, Offset); + + CurrentLayout.FieldOffsets.push_back(Offset); + + // Skip over this offset, the following comma, and any spaces. + LineStr = LineStr.substr(Idx + 1); + while (!LineStr.empty() && isspace(LineStr[0])) + LineStr = LineStr.substr(1); + } + } + + // Flush the last type/layout, if there is one. + if (!CurrentType.empty()) + Layouts[CurrentType] = CurrentLayout; +} + +bool +LayoutOverrideSource::layoutRecordType(const RecordDecl *Record, + uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap<const FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &BaseOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &VirtualBaseOffsets) +{ + // We can't override unnamed declarations. + if (!Record->getIdentifier()) + return false; + + // Check whether we have a layout for this record. + llvm::StringMap<Layout>::iterator Known = Layouts.find(Record->getName()); + if (Known == Layouts.end()) + return false; + + // Provide field layouts. + unsigned NumFields = 0; + for (RecordDecl::field_iterator F = Record->field_begin(), + FEnd = Record->field_end(); + F != FEnd; ++F, ++NumFields) { + if (NumFields >= Known->second.FieldOffsets.size()) + continue; + + FieldOffsets[*F] = Known->second.FieldOffsets[NumFields]; + } + + // Wrong number of fields. + if (NumFields != Known->second.FieldOffsets.size()) + return false; + + Size = Known->second.Size; + Alignment = Known->second.Align; + return true; +} + +void LayoutOverrideSource::dump() { + llvm::raw_ostream &OS = llvm::errs(); + for (llvm::StringMap<Layout>::iterator L = Layouts.begin(), + LEnd = Layouts.end(); + L != LEnd; ++L) { + OS << "Type: blah " << L->first() << '\n'; + OS << " Size:" << L->second.Size << '\n'; + OS << " Alignment:" << L->second.Align << '\n'; + OS << " FieldOffsets: ["; + for (unsigned I = 0, N = L->second.FieldOffsets.size(); I != N; ++I) { + if (I) + OS << ", "; + OS << L->second.FieldOffsets[I]; + } + OS << "]\n"; + } +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp b/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp index 8b585be..3fee957 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp @@ -12,6 +12,7 @@ #include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ErrorHandling.h" using namespace clang; LogDiagnosticPrinter::LogDiagnosticPrinter(raw_ostream &os, @@ -28,14 +29,13 @@ LogDiagnosticPrinter::~LogDiagnosticPrinter() { static StringRef getLevelName(DiagnosticsEngine::Level Level) { switch (Level) { - default: - return "<unknown>"; case DiagnosticsEngine::Ignored: return "ignored"; case DiagnosticsEngine::Note: return "note"; case DiagnosticsEngine::Warning: return "warning"; case DiagnosticsEngine::Error: return "error"; case DiagnosticsEngine::Fatal: return "fatal error"; } + llvm_unreachable("Invalid DiagnosticsEngine level!"); } // Escape XML characters inside the raw string. @@ -64,7 +64,7 @@ void LogDiagnosticPrinter::EndSourceFile() { return; // Write to a temporary string to ensure atomic write of diagnostic object. - llvm::SmallString<512> Msg; + SmallString<512> Msg; llvm::raw_svector_ostream OS(Msg); OS << "<dict>\n"; @@ -140,7 +140,7 @@ void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, DE.DiagnosticLevel = Level; // Format the message. - llvm::SmallString<100> MessageStr; + SmallString<100> MessageStr; Info.FormatDiagnostic(MessageStr); DE.Message = MessageStr.str(); diff --git a/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp b/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp index 8e746f6..992eeb0 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp @@ -89,7 +89,7 @@ void MultiplexASTDeserializationListener::MacroDefinitionRead( class MultiplexASTMutationListener : public ASTMutationListener { public: // Does NOT take ownership of the elements in L. - MultiplexASTMutationListener(const std::vector<ASTMutationListener*>& L); + MultiplexASTMutationListener(ArrayRef<ASTMutationListener*> L); virtual void CompletedTagDefinition(const TagDecl *D); virtual void AddedVisibleDecl(const DeclContext *DC, const Decl *D); virtual void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D); @@ -99,13 +99,18 @@ public: const FunctionDecl *D); virtual void CompletedImplicitDefinition(const FunctionDecl *D); virtual void StaticDataMemberInstantiated(const VarDecl *D); + virtual void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, + const ObjCInterfaceDecl *IFD); + virtual void AddedObjCPropertyInClassExtension(const ObjCPropertyDecl *Prop, + const ObjCPropertyDecl *OrigProp, + const ObjCCategoryDecl *ClassExt); private: std::vector<ASTMutationListener*> Listeners; }; MultiplexASTMutationListener::MultiplexASTMutationListener( - const std::vector<ASTMutationListener*>& L) - : Listeners(L) { + ArrayRef<ASTMutationListener*> L) + : Listeners(L.begin(), L.end()) { } void MultiplexASTMutationListener::CompletedTagDefinition(const TagDecl *D) { @@ -144,12 +149,26 @@ void MultiplexASTMutationListener::StaticDataMemberInstantiated( for (size_t i = 0, e = Listeners.size(); i != e; ++i) Listeners[i]->StaticDataMemberInstantiated(D); } +void MultiplexASTMutationListener::AddedObjCCategoryToInterface( + const ObjCCategoryDecl *CatD, + const ObjCInterfaceDecl *IFD) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedObjCCategoryToInterface(CatD, IFD); +} +void MultiplexASTMutationListener::AddedObjCPropertyInClassExtension( + const ObjCPropertyDecl *Prop, + const ObjCPropertyDecl *OrigProp, + const ObjCCategoryDecl *ClassExt) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedObjCPropertyInClassExtension(Prop, OrigProp, ClassExt); +} } // end namespace clang -MultiplexConsumer::MultiplexConsumer(const std::vector<ASTConsumer*>& C) - : Consumers(C), MutationListener(0), DeserializationListener(0) { +MultiplexConsumer::MultiplexConsumer(ArrayRef<ASTConsumer*> C) + : Consumers(C.begin(), C.end()), + MutationListener(0), DeserializationListener(0) { // Collect the mutation listeners and deserialization listeners of all // children, and create a multiplex listener each if so. std::vector<ASTMutationListener*> mutationListeners; @@ -183,9 +202,16 @@ void MultiplexConsumer::Initialize(ASTContext &Context) { Consumers[i]->Initialize(Context); } -void MultiplexConsumer::HandleTopLevelDecl(DeclGroupRef D) { +bool MultiplexConsumer::HandleTopLevelDecl(DeclGroupRef D) { + bool Continue = true; + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Continue = Continue && Consumers[i]->HandleTopLevelDecl(D); + return Continue; +} + +void MultiplexConsumer::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) { for (size_t i = 0, e = Consumers.size(); i != e; ++i) - Consumers[i]->HandleTopLevelDecl(D); + Consumers[i]->HandleCXXStaticMemberVarInstantiation(VD); } void MultiplexConsumer::HandleInterestingDecl(DeclGroupRef D) { @@ -203,6 +229,16 @@ void MultiplexConsumer::HandleTagDeclDefinition(TagDecl *D) { Consumers[i]->HandleTagDeclDefinition(D); } +void MultiplexConsumer::HandleCXXImplicitFunctionInstantiation(FunctionDecl *D){ + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->HandleCXXImplicitFunctionInstantiation(D); +} + +void MultiplexConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef D) { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->HandleTopLevelDeclInObjCContainer(D); +} + void MultiplexConsumer::CompleteTentativeDefinition(VarDecl *D) { for (size_t i = 0, e = Consumers.size(); i != e; ++i) Consumers[i]->CompleteTentativeDefinition(D); diff --git a/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp index 8a61f96..9e1587c 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -24,7 +24,6 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Config/config.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/ErrorHandling.h" #include <cstdio> @@ -63,7 +62,7 @@ static void PrintMacroDefinition(const IdentifierInfo &II, const MacroInfo &MI, if (MI.tokens_empty() || !MI.tokens_begin()->hasLeadingSpace()) OS << ' '; - llvm::SmallString<128> SpellingBuffer; + SmallString<128> SpellingBuffer; for (MacroInfo::tokens_iterator I = MI.tokens_begin(), E = MI.tokens_end(); I != E; ++I) { if (I->hasLeadingSpace()) @@ -90,7 +89,7 @@ private: bool EmittedTokensOnThisLine; bool EmittedMacroOnThisLine; SrcMgr::CharacteristicKind FileType; - llvm::SmallString<512> CurFilename; + SmallString<512> CurFilename; bool Initialized; bool DisableLineMarkers; bool DumpDefines; @@ -109,7 +108,7 @@ public: Initialized = false; // If we're in microsoft mode, use normal #line instead of line markers. - UseLineDirective = PP.getLangOptions().MicrosoftExt; + UseLineDirective = PP.getLangOpts().MicrosoftExt; } void SetEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } @@ -390,7 +389,6 @@ PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, MoveToLine(Loc); OS << "#pragma " << Namespace << " diagnostic "; switch (Map) { - default: llvm_unreachable("unexpected diagnostic kind"); case diag::MAP_WARNING: OS << "warning"; break; diff --git a/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp new file mode 100644 index 0000000..7bf8742 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -0,0 +1,592 @@ +//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <vector> +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/DenseSet.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/Version.h" +#include "clang/Lex/Lexer.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "clang/Frontend/DiagnosticRenderer.h" + +using namespace clang; +using namespace clang::serialized_diags; + +namespace { + +class AbbreviationMap { + llvm::DenseMap<unsigned, unsigned> Abbrevs; +public: + AbbreviationMap() {} + + void set(unsigned recordID, unsigned abbrevID) { + assert(Abbrevs.find(recordID) == Abbrevs.end() + && "Abbreviation already set."); + Abbrevs[recordID] = abbrevID; + } + + unsigned get(unsigned recordID) { + assert(Abbrevs.find(recordID) != Abbrevs.end() && + "Abbreviation not set."); + return Abbrevs[recordID]; + } +}; + +typedef llvm::SmallVector<uint64_t, 64> RecordData; +typedef llvm::SmallVectorImpl<uint64_t> RecordDataImpl; + +class SDiagsWriter; + +class SDiagsRenderer : public DiagnosticNoteRenderer { + SDiagsWriter &Writer; + RecordData &Record; +public: + SDiagsRenderer(SDiagsWriter &Writer, RecordData &Record, + const SourceManager &SM, + const LangOptions &LangOpts, + const DiagnosticOptions &DiagOpts) + : DiagnosticNoteRenderer(SM, LangOpts, DiagOpts), + Writer(Writer), Record(Record){} + + virtual ~SDiagsRenderer() {} + +protected: + virtual void emitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<CharSourceRange> Ranges, + DiagOrStoredDiag D); + + virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges) {} + + void emitNote(SourceLocation Loc, StringRef Message); + + virtual void emitCodeContext(SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange>& Ranges, + ArrayRef<FixItHint> Hints); + + virtual void beginDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level); + virtual void endDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level); +}; + +class SDiagsWriter : public DiagnosticConsumer { + friend class SDiagsRenderer; +public: + explicit SDiagsWriter(llvm::raw_ostream *os, const DiagnosticOptions &diags) + : LangOpts(0), DiagOpts(diags), + Stream(Buffer), OS(os), inNonNoteDiagnostic(false) + { + EmitPreamble(); + } + + ~SDiagsWriter() {} + + void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info); + + void BeginSourceFile(const LangOptions &LO, + const Preprocessor *PP) { + LangOpts = &LO; + } + + virtual void finish(); + + DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { + // It makes no sense to clone this. + return 0; + } + +private: + /// \brief Emit the preamble for the serialized diagnostics. + void EmitPreamble(); + + /// \brief Emit the BLOCKINFO block. + void EmitBlockInfoBlock(); + + /// \brief Emit the META data block. + void EmitMetaBlock(); + + /// \brief Emit a record for a CharSourceRange. + void EmitCharSourceRange(CharSourceRange R, const SourceManager &SM); + + /// \brief Emit the string information for the category. + unsigned getEmitCategory(unsigned category = 0); + + /// \brief Emit the string information for diagnostic flags. + unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, + unsigned DiagID = 0); + + /// \brief Emit (lazily) the file string and retrieved the file identifier. + unsigned getEmitFile(const char *Filename); + + /// \brief Add SourceLocation information the specified record. + void AddLocToRecord(SourceLocation Loc, const SourceManager &SM, + PresumedLoc PLoc, RecordDataImpl &Record, + unsigned TokSize = 0); + + /// \brief Add SourceLocation information the specified record. + void AddLocToRecord(SourceLocation Loc, RecordDataImpl &Record, + const SourceManager &SM, + unsigned TokSize = 0) { + AddLocToRecord(Loc, SM, SM.getPresumedLoc(Loc), Record, TokSize); + } + + /// \brief Add CharSourceRange information the specified record. + void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record, + const SourceManager &SM); + + /// \brief The version of the diagnostics file. + enum { Version = 1 }; + + const LangOptions *LangOpts; + const DiagnosticOptions &DiagOpts; + + /// \brief The byte buffer for the serialized content. + SmallString<1024> Buffer; + + /// \brief The BitStreamWriter for the serialized diagnostics. + llvm::BitstreamWriter Stream; + + /// \brief The name of the diagnostics file. + OwningPtr<llvm::raw_ostream> OS; + + /// \brief The set of constructed record abbreviations. + AbbreviationMap Abbrevs; + + /// \brief A utility buffer for constructing record content. + RecordData Record; + + /// \brief A text buffer for rendering diagnostic text. + SmallString<256> diagBuf; + + /// \brief The collection of diagnostic categories used. + llvm::DenseSet<unsigned> Categories; + + /// \brief The collection of files used. + llvm::DenseMap<const char *, unsigned> Files; + + typedef llvm::DenseMap<const void *, std::pair<unsigned, llvm::StringRef> > + DiagFlagsTy; + + /// \brief Map for uniquing strings. + DiagFlagsTy DiagFlags; + + /// \brief Flag indicating whether or not we are in the process of + /// emitting a non-note diagnostic. + bool inNonNoteDiagnostic; +}; +} // end anonymous namespace + +namespace clang { +namespace serialized_diags { +DiagnosticConsumer *create(llvm::raw_ostream *OS, + const DiagnosticOptions &diags) { + return new SDiagsWriter(OS, diags); +} +} // end namespace serialized_diags +} // end namespace clang + +//===----------------------------------------------------------------------===// +// Serialization methods. +//===----------------------------------------------------------------------===// + +/// \brief Emits a block ID in the BLOCKINFO block. +static void EmitBlockID(unsigned ID, const char *Name, + llvm::BitstreamWriter &Stream, + RecordDataImpl &Record) { + Record.clear(); + Record.push_back(ID); + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record); + + // Emit the block name if present. + if (Name == 0 || Name[0] == 0) + return; + + Record.clear(); + + while (*Name) + Record.push_back(*Name++); + + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record); +} + +/// \brief Emits a record ID in the BLOCKINFO block. +static void EmitRecordID(unsigned ID, const char *Name, + llvm::BitstreamWriter &Stream, + RecordDataImpl &Record){ + Record.clear(); + Record.push_back(ID); + + while (*Name) + Record.push_back(*Name++); + + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); +} + +void SDiagsWriter::AddLocToRecord(SourceLocation Loc, + const SourceManager &SM, + PresumedLoc PLoc, + RecordDataImpl &Record, + unsigned TokSize) { + if (PLoc.isInvalid()) { + // Emit a "sentinel" location. + Record.push_back((unsigned)0); // File. + Record.push_back((unsigned)0); // Line. + Record.push_back((unsigned)0); // Column. + Record.push_back((unsigned)0); // Offset. + return; + } + + Record.push_back(getEmitFile(PLoc.getFilename())); + Record.push_back(PLoc.getLine()); + Record.push_back(PLoc.getColumn()+TokSize); + Record.push_back(SM.getFileOffset(Loc)); +} + +void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range, + RecordDataImpl &Record, + const SourceManager &SM) { + AddLocToRecord(Range.getBegin(), Record, SM); + unsigned TokSize = 0; + if (Range.isTokenRange()) + TokSize = Lexer::MeasureTokenLength(Range.getEnd(), + SM, *LangOpts); + + AddLocToRecord(Range.getEnd(), Record, SM, TokSize); +} + +unsigned SDiagsWriter::getEmitFile(const char *FileName){ + if (!FileName) + return 0; + + unsigned &entry = Files[FileName]; + if (entry) + return entry; + + // Lazily generate the record for the file. + entry = Files.size(); + RecordData Record; + Record.push_back(RECORD_FILENAME); + Record.push_back(entry); + Record.push_back(0); // For legacy. + Record.push_back(0); // For legacy. + StringRef Name(FileName); + Record.push_back(Name.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FILENAME), Record, Name); + + return entry; +} + +void SDiagsWriter::EmitCharSourceRange(CharSourceRange R, + const SourceManager &SM) { + Record.clear(); + Record.push_back(RECORD_SOURCE_RANGE); + AddCharSourceRangeToRecord(R, Record, SM); + Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_SOURCE_RANGE), Record); +} + +/// \brief Emits the preamble of the diagnostics file. +void SDiagsWriter::EmitPreamble() { + // Emit the file header. + Stream.Emit((unsigned)'D', 8); + Stream.Emit((unsigned)'I', 8); + Stream.Emit((unsigned)'A', 8); + Stream.Emit((unsigned)'G', 8); + + EmitBlockInfoBlock(); + EmitMetaBlock(); +} + +static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) { + using namespace llvm; + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // File ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset; +} + +static void AddRangeLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) { + AddSourceLocationAbbrev(Abbrev); + AddSourceLocationAbbrev(Abbrev); +} + +void SDiagsWriter::EmitBlockInfoBlock() { + Stream.EnterBlockInfoBlock(3); + + using namespace llvm; + + // ==---------------------------------------------------------------------==// + // The subsequent records and Abbrevs are for the "Meta" block. + // ==---------------------------------------------------------------------==// + + EmitBlockID(BLOCK_META, "Meta", Stream, Record); + EmitRecordID(RECORD_VERSION, "Version", Stream, Record); + BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_VERSION)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + Abbrevs.set(RECORD_VERSION, Stream.EmitBlockInfoAbbrev(BLOCK_META, Abbrev)); + + // ==---------------------------------------------------------------------==// + // The subsequent records and Abbrevs are for the "Diagnostic" block. + // ==---------------------------------------------------------------------==// + + EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record); + EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record); + EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record); + EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record); + EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record); + EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record); + EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record); + + // Emit abbreviation for RECORD_DIAG. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level. + AddSourceLocationAbbrev(Abbrev); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text. + Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + + // Emit abbrevation for RECORD_CATEGORY. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text. + Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + + // Emit abbrevation for RECORD_SOURCE_RANGE. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE)); + AddRangeLocationAbbrev(Abbrev); + Abbrevs.set(RECORD_SOURCE_RANGE, + Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + + // Emit the abbreviation for RECORD_DIAG_FLAG. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text. + Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, + Abbrev)); + + // Emit the abbreviation for RECORD_FILENAME. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped file ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modifcation time. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text. + Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, + Abbrev)); + + // Emit the abbreviation for RECORD_FIXIT. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_FIXIT)); + AddRangeLocationAbbrev(Abbrev); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // FixIt text. + Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, + Abbrev)); + + Stream.ExitBlock(); +} + +void SDiagsWriter::EmitMetaBlock() { + Stream.EnterSubblock(BLOCK_META, 3); + Record.clear(); + Record.push_back(RECORD_VERSION); + Record.push_back(Version); + Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record); + Stream.ExitBlock(); +} + +unsigned SDiagsWriter::getEmitCategory(unsigned int category) { + if (Categories.count(category)) + return category; + + Categories.insert(category); + + // We use a local version of 'Record' so that we can be generating + // another record when we lazily generate one for the category entry. + RecordData Record; + Record.push_back(RECORD_CATEGORY); + Record.push_back(category); + StringRef catName = DiagnosticIDs::getCategoryNameFromID(category); + Record.push_back(catName.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_CATEGORY), Record, catName); + + return category; +} + +unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, + unsigned DiagID) { + if (DiagLevel == DiagnosticsEngine::Note) + return 0; // No flag for notes. + + StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(DiagID); + if (FlagName.empty()) + return 0; + + // Here we assume that FlagName points to static data whose pointer + // value is fixed. This allows us to unique by diagnostic groups. + const void *data = FlagName.data(); + std::pair<unsigned, StringRef> &entry = DiagFlags[data]; + if (entry.first == 0) { + entry.first = DiagFlags.size(); + entry.second = FlagName; + + // Lazily emit the string in a separate record. + RecordData Record; + Record.push_back(RECORD_DIAG_FLAG); + Record.push_back(entry.first); + Record.push_back(FlagName.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG), + Record, FlagName); + } + + return entry.first; +} + +void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) { + if (DiagLevel != DiagnosticsEngine::Note) { + if (inNonNoteDiagnostic) { + // We have encountered a non-note diagnostic. Finish up the previous + // diagnostic block before starting a new one. + Stream.ExitBlock(); + } + inNonNoteDiagnostic = true; + } + + // Compute the diagnostic text. + diagBuf.clear(); + Info.FormatDiagnostic(diagBuf); + + SourceManager &SM = Info.getSourceManager(); + SDiagsRenderer Renderer(*this, Record, SM, *LangOpts, DiagOpts); + Renderer.emitDiagnostic(Info.getLocation(), DiagLevel, + diagBuf.str(), + Info.getRanges(), + llvm::makeArrayRef(Info.getFixItHints(), + Info.getNumFixItHints()), + &Info); +} + +void +SDiagsRenderer::emitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<clang::CharSourceRange> Ranges, + DiagOrStoredDiag D) { + // Emit the RECORD_DIAG record. + Writer.Record.clear(); + Writer.Record.push_back(RECORD_DIAG); + Writer.Record.push_back(Level); + Writer.AddLocToRecord(Loc, SM, PLoc, Record); + + if (const Diagnostic *Info = D.dyn_cast<const Diagnostic*>()) { + // Emit the category string lazily and get the category ID. + unsigned DiagID = DiagnosticIDs::getCategoryNumberForDiag(Info->getID()); + Writer.Record.push_back(Writer.getEmitCategory(DiagID)); + // Emit the diagnostic flag string lazily and get the mapped ID. + Writer.Record.push_back(Writer.getEmitDiagnosticFlag(Level, Info->getID())); + } + else { + Writer.Record.push_back(Writer.getEmitCategory()); + Writer.Record.push_back(Writer.getEmitDiagnosticFlag(Level)); + } + + Writer.Record.push_back(Message.size()); + Writer.Stream.EmitRecordWithBlob(Writer.Abbrevs.get(RECORD_DIAG), + Writer.Record, Message); +} + +void SDiagsRenderer::beginDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) { + Writer.Stream.EnterSubblock(BLOCK_DIAG, 4); +} + +void SDiagsRenderer::endDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) { + if (D && Level != DiagnosticsEngine::Note) + return; + Writer.Stream.ExitBlock(); +} + +void SDiagsRenderer::emitCodeContext(SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints) { + // Emit Source Ranges. + for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end(); + it != ei; ++it) { + if (it->isValid()) + Writer.EmitCharSourceRange(*it, SM); + } + + // Emit FixIts. + for (ArrayRef<FixItHint>::iterator it = Hints.begin(), et = Hints.end(); + it != et; ++it) { + const FixItHint &fix = *it; + if (fix.isNull()) + continue; + Writer.Record.clear(); + Writer.Record.push_back(RECORD_FIXIT); + Writer.AddCharSourceRangeToRecord(fix.RemoveRange, Record, SM); + Writer.Record.push_back(fix.CodeToInsert.size()); + Writer.Stream.EmitRecordWithBlob(Writer.Abbrevs.get(RECORD_FIXIT), Record, + fix.CodeToInsert); + } +} + +void SDiagsRenderer::emitNote(SourceLocation Loc, StringRef Message) { + Writer.Stream.EnterSubblock(BLOCK_DIAG, 4); + RecordData Record; + Record.push_back(RECORD_DIAG); + Record.push_back(DiagnosticsEngine::Note); + Writer.AddLocToRecord(Loc, Record, SM); + Record.push_back(Writer.getEmitCategory()); + Record.push_back(Writer.getEmitDiagnosticFlag(DiagnosticsEngine::Note)); + Record.push_back(Message.size()); + Writer.Stream.EmitRecordWithBlob(Writer.Abbrevs.get(RECORD_DIAG), + Record, Message); + Writer.Stream.ExitBlock(); +} + +void SDiagsWriter::finish() { + if (inNonNoteDiagnostic) { + // Finish off any diagnostics we were in the process of emitting. + Stream.ExitBlock(); + inNonNoteDiagnostic = false; + } + + // Write the generated bitstream to "Out". + OS->write((char *)&Buffer.front(), Buffer.size()); + OS->flush(); + + OS.reset(0); +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp new file mode 100644 index 0000000..9f5dcb4 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp @@ -0,0 +1,881 @@ +//===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/TextDiagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Lex/Lexer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/ADT/SmallString.h" +#include <algorithm> +using namespace clang; + +static const enum raw_ostream::Colors noteColor = + raw_ostream::BLACK; +static const enum raw_ostream::Colors fixitColor = + raw_ostream::GREEN; +static const enum raw_ostream::Colors caretColor = + raw_ostream::GREEN; +static const enum raw_ostream::Colors warningColor = + raw_ostream::MAGENTA; +static const enum raw_ostream::Colors errorColor = raw_ostream::RED; +static const enum raw_ostream::Colors fatalColor = raw_ostream::RED; +// Used for changing only the bold attribute. +static const enum raw_ostream::Colors savedColor = + raw_ostream::SAVEDCOLOR; + +/// \brief Number of spaces to indent when word-wrapping. +const unsigned WordWrapIndentation = 6; + +/// \brief When the source code line we want to print is too long for +/// the terminal, select the "interesting" region. +static void selectInterestingSourceRegion(std::string &SourceLine, + std::string &CaretLine, + std::string &FixItInsertionLine, + unsigned EndOfCaretToken, + unsigned Columns) { + unsigned MaxSize = std::max(SourceLine.size(), + std::max(CaretLine.size(), + FixItInsertionLine.size())); + if (MaxSize > SourceLine.size()) + SourceLine.resize(MaxSize, ' '); + if (MaxSize > CaretLine.size()) + CaretLine.resize(MaxSize, ' '); + if (!FixItInsertionLine.empty() && MaxSize > FixItInsertionLine.size()) + FixItInsertionLine.resize(MaxSize, ' '); + + // Find the slice that we need to display the full caret line + // correctly. + unsigned CaretStart = 0, CaretEnd = CaretLine.size(); + for (; CaretStart != CaretEnd; ++CaretStart) + if (!isspace(CaretLine[CaretStart])) + break; + + for (; CaretEnd != CaretStart; --CaretEnd) + if (!isspace(CaretLine[CaretEnd - 1])) + break; + + // Make sure we don't chop the string shorter than the caret token + // itself. + if (CaretEnd < EndOfCaretToken) + CaretEnd = EndOfCaretToken; + + // If we have a fix-it line, make sure the slice includes all of the + // fix-it information. + if (!FixItInsertionLine.empty()) { + unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size(); + for (; FixItStart != FixItEnd; ++FixItStart) + if (!isspace(FixItInsertionLine[FixItStart])) + break; + + for (; FixItEnd != FixItStart; --FixItEnd) + if (!isspace(FixItInsertionLine[FixItEnd - 1])) + break; + + if (FixItStart < CaretStart) + CaretStart = FixItStart; + if (FixItEnd > CaretEnd) + CaretEnd = FixItEnd; + } + + // CaretLine[CaretStart, CaretEnd) contains all of the interesting + // parts of the caret line. While this slice is smaller than the + // number of columns we have, try to grow the slice to encompass + // more context. + + // If the end of the interesting region comes before we run out of + // space in the terminal, start at the beginning of the line. + if (Columns > 3 && CaretEnd < Columns - 3) + CaretStart = 0; + + unsigned TargetColumns = Columns; + if (TargetColumns > 8) + TargetColumns -= 8; // Give us extra room for the ellipses. + unsigned SourceLength = SourceLine.size(); + while ((CaretEnd - CaretStart) < TargetColumns) { + bool ExpandedRegion = false; + // Move the start of the interesting region left until we've + // pulled in something else interesting. + if (CaretStart == 1) + CaretStart = 0; + else if (CaretStart > 1) { + unsigned NewStart = CaretStart - 1; + + // Skip over any whitespace we see here; we're looking for + // another bit of interesting text. + while (NewStart && isspace(SourceLine[NewStart])) + --NewStart; + + // Skip over this bit of "interesting" text. + while (NewStart && !isspace(SourceLine[NewStart])) + --NewStart; + + // Move up to the non-whitespace character we just saw. + if (NewStart) + ++NewStart; + + // If we're still within our limit, update the starting + // position within the source/caret line. + if (CaretEnd - NewStart <= TargetColumns) { + CaretStart = NewStart; + ExpandedRegion = true; + } + } + + // Move the end of the interesting region right until we've + // pulled in something else interesting. + if (CaretEnd != SourceLength) { + assert(CaretEnd < SourceLength && "Unexpected caret position!"); + unsigned NewEnd = CaretEnd; + + // Skip over any whitespace we see here; we're looking for + // another bit of interesting text. + while (NewEnd != SourceLength && isspace(SourceLine[NewEnd - 1])) + ++NewEnd; + + // Skip over this bit of "interesting" text. + while (NewEnd != SourceLength && !isspace(SourceLine[NewEnd - 1])) + ++NewEnd; + + if (NewEnd - CaretStart <= TargetColumns) { + CaretEnd = NewEnd; + ExpandedRegion = true; + } + } + + if (!ExpandedRegion) + break; + } + + // [CaretStart, CaretEnd) is the slice we want. Update the various + // output lines to show only this slice, with two-space padding + // before the lines so that it looks nicer. + if (CaretEnd < SourceLine.size()) + SourceLine.replace(CaretEnd, std::string::npos, "..."); + if (CaretEnd < CaretLine.size()) + CaretLine.erase(CaretEnd, std::string::npos); + if (FixItInsertionLine.size() > CaretEnd) + FixItInsertionLine.erase(CaretEnd, std::string::npos); + + if (CaretStart > 2) { + SourceLine.replace(0, CaretStart, " ..."); + CaretLine.replace(0, CaretStart, " "); + if (FixItInsertionLine.size() >= CaretStart) + FixItInsertionLine.replace(0, CaretStart, " "); + } +} + +/// \brief Skip over whitespace in the string, starting at the given +/// index. +/// +/// \returns The index of the first non-whitespace character that is +/// greater than or equal to Idx or, if no such character exists, +/// returns the end of the string. +static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) { + while (Idx < Length && isspace(Str[Idx])) + ++Idx; + return Idx; +} + +/// \brief If the given character is the start of some kind of +/// balanced punctuation (e.g., quotes or parentheses), return the +/// character that will terminate the punctuation. +/// +/// \returns The ending punctuation character, if any, or the NULL +/// character if the input character does not start any punctuation. +static inline char findMatchingPunctuation(char c) { + switch (c) { + case '\'': return '\''; + case '`': return '\''; + case '"': return '"'; + case '(': return ')'; + case '[': return ']'; + case '{': return '}'; + default: break; + } + + return 0; +} + +/// \brief Find the end of the word starting at the given offset +/// within a string. +/// +/// \returns the index pointing one character past the end of the +/// word. +static unsigned findEndOfWord(unsigned Start, StringRef Str, + unsigned Length, unsigned Column, + unsigned Columns) { + assert(Start < Str.size() && "Invalid start position!"); + unsigned End = Start + 1; + + // If we are already at the end of the string, take that as the word. + if (End == Str.size()) + return End; + + // Determine if the start of the string is actually opening + // punctuation, e.g., a quote or parentheses. + char EndPunct = findMatchingPunctuation(Str[Start]); + if (!EndPunct) { + // This is a normal word. Just find the first space character. + while (End < Length && !isspace(Str[End])) + ++End; + return End; + } + + // We have the start of a balanced punctuation sequence (quotes, + // parentheses, etc.). Determine the full sequence is. + SmallString<16> PunctuationEndStack; + PunctuationEndStack.push_back(EndPunct); + while (End < Length && !PunctuationEndStack.empty()) { + if (Str[End] == PunctuationEndStack.back()) + PunctuationEndStack.pop_back(); + else if (char SubEndPunct = findMatchingPunctuation(Str[End])) + PunctuationEndStack.push_back(SubEndPunct); + + ++End; + } + + // Find the first space character after the punctuation ended. + while (End < Length && !isspace(Str[End])) + ++End; + + unsigned PunctWordLength = End - Start; + if (// If the word fits on this line + Column + PunctWordLength <= Columns || + // ... or the word is "short enough" to take up the next line + // without too much ugly white space + PunctWordLength < Columns/3) + return End; // Take the whole thing as a single "word". + + // The whole quoted/parenthesized string is too long to print as a + // single "word". Instead, find the "word" that starts just after + // the punctuation and use that end-point instead. This will recurse + // until it finds something small enough to consider a word. + return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns); +} + +/// \brief Print the given string to a stream, word-wrapping it to +/// some number of columns in the process. +/// +/// \param OS the stream to which the word-wrapping string will be +/// emitted. +/// \param Str the string to word-wrap and output. +/// \param Columns the number of columns to word-wrap to. +/// \param Column the column number at which the first character of \p +/// Str will be printed. This will be non-zero when part of the first +/// line has already been printed. +/// \param Indentation the number of spaces to indent any lines beyond +/// the first line. +/// \returns true if word-wrapping was required, or false if the +/// string fit on the first line. +static bool printWordWrapped(raw_ostream &OS, StringRef Str, + unsigned Columns, + unsigned Column = 0, + unsigned Indentation = WordWrapIndentation) { + const unsigned Length = std::min(Str.find('\n'), Str.size()); + + // The string used to indent each line. + SmallString<16> IndentStr; + IndentStr.assign(Indentation, ' '); + bool Wrapped = false; + for (unsigned WordStart = 0, WordEnd; WordStart < Length; + WordStart = WordEnd) { + // Find the beginning of the next word. + WordStart = skipWhitespace(WordStart, Str, Length); + if (WordStart == Length) + break; + + // Find the end of this word. + WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns); + + // Does this word fit on the current line? + unsigned WordLength = WordEnd - WordStart; + if (Column + WordLength < Columns) { + // This word fits on the current line; print it there. + if (WordStart) { + OS << ' '; + Column += 1; + } + OS << Str.substr(WordStart, WordLength); + Column += WordLength; + continue; + } + + // This word does not fit on the current line, so wrap to the next + // line. + OS << '\n'; + OS.write(&IndentStr[0], Indentation); + OS << Str.substr(WordStart, WordLength); + Column = Indentation + WordLength; + Wrapped = true; + } + + // Append any remaning text from the message with its existing formatting. + OS << Str.substr(Length); + + return Wrapped; +} + +TextDiagnostic::TextDiagnostic(raw_ostream &OS, + const SourceManager &SM, + const LangOptions &LangOpts, + const DiagnosticOptions &DiagOpts) + : DiagnosticRenderer(SM, LangOpts, DiagOpts), OS(OS) {} + +TextDiagnostic::~TextDiagnostic() {} + +void +TextDiagnostic::emitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<clang::CharSourceRange> Ranges, + DiagOrStoredDiag D) { + uint64_t StartOfLocationInfo = OS.tell(); + + // Emit the location of this particular diagnostic. + emitDiagnosticLoc(Loc, PLoc, Level, Ranges); + + if (DiagOpts.ShowColors) + OS.resetColor(); + + printDiagnosticLevel(OS, Level, DiagOpts.ShowColors); + printDiagnosticMessage(OS, Level, Message, + OS.tell() - StartOfLocationInfo, + DiagOpts.MessageLength, DiagOpts.ShowColors); +} + +/*static*/ void +TextDiagnostic::printDiagnosticLevel(raw_ostream &OS, + DiagnosticsEngine::Level Level, + bool ShowColors) { + if (ShowColors) { + // Print diagnostic category in bold and color + switch (Level) { + case DiagnosticsEngine::Ignored: + llvm_unreachable("Invalid diagnostic type"); + case DiagnosticsEngine::Note: OS.changeColor(noteColor, true); break; + case DiagnosticsEngine::Warning: OS.changeColor(warningColor, true); break; + case DiagnosticsEngine::Error: OS.changeColor(errorColor, true); break; + case DiagnosticsEngine::Fatal: OS.changeColor(fatalColor, true); break; + } + } + + switch (Level) { + case DiagnosticsEngine::Ignored: + llvm_unreachable("Invalid diagnostic type"); + case DiagnosticsEngine::Note: OS << "note: "; break; + case DiagnosticsEngine::Warning: OS << "warning: "; break; + case DiagnosticsEngine::Error: OS << "error: "; break; + case DiagnosticsEngine::Fatal: OS << "fatal error: "; break; + } + + if (ShowColors) + OS.resetColor(); +} + +/*static*/ void +TextDiagnostic::printDiagnosticMessage(raw_ostream &OS, + DiagnosticsEngine::Level Level, + StringRef Message, + unsigned CurrentColumn, unsigned Columns, + bool ShowColors) { + if (ShowColors) { + // Print warnings, errors and fatal errors in bold, no color + switch (Level) { + case DiagnosticsEngine::Warning: OS.changeColor(savedColor, true); break; + case DiagnosticsEngine::Error: OS.changeColor(savedColor, true); break; + case DiagnosticsEngine::Fatal: OS.changeColor(savedColor, true); break; + default: break; //don't bold notes + } + } + + if (Columns) + printWordWrapped(OS, Message, Columns, CurrentColumn); + else + OS << Message; + + if (ShowColors) + OS.resetColor(); + OS << '\n'; +} + +/// \brief Print out the file/line/column information and include trace. +/// +/// This method handlen the emission of the diagnostic location information. +/// This includes extracting as much location information as is present for +/// the diagnostic and printing it, as well as any include stack or source +/// ranges necessary. +void TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges) { + if (PLoc.isInvalid()) { + // At least print the file name if available: + FileID FID = SM.getFileID(Loc); + if (!FID.isInvalid()) { + const FileEntry* FE = SM.getFileEntryForID(FID); + if (FE && FE->getName()) { + OS << FE->getName(); + if (FE->getDevice() == 0 && FE->getInode() == 0 + && FE->getFileMode() == 0) { + // in PCH is a guess, but a good one: + OS << " (in PCH)"; + } + OS << ": "; + } + } + return; + } + unsigned LineNo = PLoc.getLine(); + + if (!DiagOpts.ShowLocation) + return; + + if (DiagOpts.ShowColors) + OS.changeColor(savedColor, true); + + OS << PLoc.getFilename(); + switch (DiagOpts.Format) { + case DiagnosticOptions::Clang: OS << ':' << LineNo; break; + case DiagnosticOptions::Msvc: OS << '(' << LineNo; break; + case DiagnosticOptions::Vi: OS << " +" << LineNo; break; + } + + if (DiagOpts.ShowColumn) + // Compute the column number. + if (unsigned ColNo = PLoc.getColumn()) { + if (DiagOpts.Format == DiagnosticOptions::Msvc) { + OS << ','; + ColNo--; + } else + OS << ':'; + OS << ColNo; + } + switch (DiagOpts.Format) { + case DiagnosticOptions::Clang: + case DiagnosticOptions::Vi: OS << ':'; break; + case DiagnosticOptions::Msvc: OS << ") : "; break; + } + + if (DiagOpts.ShowSourceRanges && !Ranges.empty()) { + FileID CaretFileID = + SM.getFileID(SM.getExpansionLoc(Loc)); + bool PrintedRange = false; + + for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(), + RE = Ranges.end(); + RI != RE; ++RI) { + // Ignore invalid ranges. + if (!RI->isValid()) continue; + + SourceLocation B = SM.getExpansionLoc(RI->getBegin()); + SourceLocation E = SM.getExpansionLoc(RI->getEnd()); + + // If the End location and the start location are the same and are a + // macro location, then the range was something that came from a + // macro expansion or _Pragma. If this is an object-like macro, the + // best we can do is to highlight the range. If this is a + // function-like macro, we'd also like to highlight the arguments. + if (B == E && RI->getEnd().isMacroID()) + E = SM.getExpansionRange(RI->getEnd()).second; + + std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); + std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); + + // If the start or end of the range is in another file, just discard + // it. + if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) + continue; + + // Add in the length of the token, so that we cover multi-char + // tokens. + unsigned TokSize = 0; + if (RI->isTokenRange()) + TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); + + OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':' + << SM.getColumnNumber(BInfo.first, BInfo.second) << '-' + << SM.getLineNumber(EInfo.first, EInfo.second) << ':' + << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) + << '}'; + PrintedRange = true; + } + + if (PrintedRange) + OS << ':'; + } + OS << ' '; +} + +void TextDiagnostic::emitBasicNote(StringRef Message) { + // FIXME: Emit this as a real note diagnostic. + // FIXME: Format an actual diagnostic rather than a hard coded string. + OS << "note: " << Message << "\n"; +} + +void TextDiagnostic::emitIncludeLocation(SourceLocation Loc, + PresumedLoc PLoc) { + if (DiagOpts.ShowLocation) + OS << "In file included from " << PLoc.getFilename() << ':' + << PLoc.getLine() << ":\n"; + else + OS << "In included file:\n"; +} + +/// \brief Emit a code snippet and caret line. +/// +/// This routine emits a single line's code snippet and caret line.. +/// +/// \param Loc The location for the caret. +/// \param Ranges The underlined ranges for this code snippet. +/// \param Hints The FixIt hints active for this diagnostic. +void TextDiagnostic::emitSnippetAndCaret( + SourceLocation Loc, DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange>& Ranges, + ArrayRef<FixItHint> Hints) { + assert(!Loc.isInvalid() && "must have a valid source location here"); + assert(Loc.isFileID() && "must have a file location here"); + + // If caret diagnostics are enabled and we have location, we want to + // emit the caret. However, we only do this if the location moved + // from the last diagnostic, if the last diagnostic was a note that + // was part of a different warning or error diagnostic, or if the + // diagnostic has ranges. We don't want to emit the same caret + // multiple times if one loc has multiple diagnostics. + if (!DiagOpts.ShowCarets) + return; + if (Loc == LastLoc && Ranges.empty() && Hints.empty() && + (LastLevel != DiagnosticsEngine::Note || Level == LastLevel)) + return; + + // Decompose the location into a FID/Offset pair. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + FileID FID = LocInfo.first; + unsigned FileOffset = LocInfo.second; + + // Get information about the buffer it points into. + bool Invalid = false; + const char *BufStart = SM.getBufferData(FID, &Invalid).data(); + if (Invalid) + return; + + unsigned LineNo = SM.getLineNumber(FID, FileOffset); + unsigned ColNo = SM.getColumnNumber(FID, FileOffset); + unsigned CaretEndColNo + = ColNo + Lexer::MeasureTokenLength(Loc, SM, LangOpts); + + // Rewind from the current position to the start of the line. + const char *TokPtr = BufStart+FileOffset; + const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. + + + // Compute the line end. Scan forward from the error position to the end of + // the line. + const char *LineEnd = TokPtr; + while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') + ++LineEnd; + + // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past + // the source line length as currently being computed. See + // test/Misc/message-length.c. + CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart)); + + // Copy the line of code into an std::string for ease of manipulation. + std::string SourceLine(LineStart, LineEnd); + + // Create a line for the caret that is filled with spaces that is the same + // length as the line of source code. + std::string CaretLine(LineEnd-LineStart, ' '); + + // Highlight all of the characters covered by Ranges with ~ characters. + for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); + I != E; ++I) + highlightRange(*I, LineNo, FID, SourceLine, CaretLine); + + // Next, insert the caret itself. + if (ColNo-1 < CaretLine.size()) + CaretLine[ColNo-1] = '^'; + else + CaretLine.push_back('^'); + + expandTabs(SourceLine, CaretLine); + + // If we are in -fdiagnostics-print-source-range-info mode, we are trying + // to produce easily machine parsable output. Add a space before the + // source line and the caret to make it trivial to tell the main diagnostic + // line from what the user is intended to see. + if (DiagOpts.ShowSourceRanges) { + SourceLine = ' ' + SourceLine; + CaretLine = ' ' + CaretLine; + } + + std::string FixItInsertionLine = buildFixItInsertionLine(LineNo, + LineStart, LineEnd, + Hints); + + // If the source line is too long for our terminal, select only the + // "interesting" source region within that line. + unsigned Columns = DiagOpts.MessageLength; + if (Columns && SourceLine.size() > Columns) + selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine, + CaretEndColNo, Columns); + + // Finally, remove any blank spaces from the end of CaretLine. + while (CaretLine[CaretLine.size()-1] == ' ') + CaretLine.erase(CaretLine.end()-1); + + // Emit what we have computed. + OS << SourceLine << '\n'; + + if (DiagOpts.ShowColors) + OS.changeColor(caretColor, true); + OS << CaretLine << '\n'; + if (DiagOpts.ShowColors) + OS.resetColor(); + + if (!FixItInsertionLine.empty()) { + if (DiagOpts.ShowColors) + // Print fixit line in color + OS.changeColor(fixitColor, false); + if (DiagOpts.ShowSourceRanges) + OS << ' '; + OS << FixItInsertionLine << '\n'; + if (DiagOpts.ShowColors) + OS.resetColor(); + } + + // Print out any parseable fixit information requested by the options. + emitParseableFixits(Hints); +} + +/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo. +void TextDiagnostic::highlightRange(const CharSourceRange &R, + unsigned LineNo, FileID FID, + const std::string &SourceLine, + std::string &CaretLine) { + assert(CaretLine.size() == SourceLine.size() && + "Expect a correspondence between source and caret line!"); + if (!R.isValid()) return; + + SourceLocation Begin = SM.getExpansionLoc(R.getBegin()); + SourceLocation End = SM.getExpansionLoc(R.getEnd()); + + // If the End location and the start location are the same and are a macro + // location, then the range was something that came from a macro expansion + // or _Pragma. If this is an object-like macro, the best we can do is to + // highlight the range. If this is a function-like macro, we'd also like to + // highlight the arguments. + if (Begin == End && R.getEnd().isMacroID()) + End = SM.getExpansionRange(R.getEnd()).second; + + unsigned StartLineNo = SM.getExpansionLineNumber(Begin); + if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) + return; // No intersection. + + unsigned EndLineNo = SM.getExpansionLineNumber(End); + if (EndLineNo < LineNo || SM.getFileID(End) != FID) + return; // No intersection. + + // Compute the column number of the start. + unsigned StartColNo = 0; + if (StartLineNo == LineNo) { + StartColNo = SM.getExpansionColumnNumber(Begin); + if (StartColNo) --StartColNo; // Zero base the col #. + } + + // Compute the column number of the end. + unsigned EndColNo = CaretLine.size(); + if (EndLineNo == LineNo) { + EndColNo = SM.getExpansionColumnNumber(End); + if (EndColNo) { + --EndColNo; // Zero base the col #. + + // Add in the length of the token, so that we cover multi-char tokens if + // this is a token range. + if (R.isTokenRange()) + EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts); + } else { + EndColNo = CaretLine.size(); + } + } + + assert(StartColNo <= EndColNo && "Invalid range!"); + + // Check that a token range does not highlight only whitespace. + if (R.isTokenRange()) { + // Pick the first non-whitespace column. + while (StartColNo < SourceLine.size() && + (SourceLine[StartColNo] == ' ' || SourceLine[StartColNo] == '\t')) + ++StartColNo; + + // Pick the last non-whitespace column. + if (EndColNo > SourceLine.size()) + EndColNo = SourceLine.size(); + while (EndColNo-1 && + (SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t')) + --EndColNo; + + // If the start/end passed each other, then we are trying to highlight a + // range that just exists in whitespace, which must be some sort of other + // bug. + assert(StartColNo <= EndColNo && "Trying to highlight whitespace??"); + } + + // Fill the range with ~'s. + for (unsigned i = StartColNo; i < EndColNo; ++i) + CaretLine[i] = '~'; +} + +std::string TextDiagnostic::buildFixItInsertionLine(unsigned LineNo, + const char *LineStart, + const char *LineEnd, + ArrayRef<FixItHint> Hints) { + std::string FixItInsertionLine; + if (Hints.empty() || !DiagOpts.ShowFixits) + return FixItInsertionLine; + + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + if (!I->CodeToInsert.empty()) { + // We have an insertion hint. Determine whether the inserted + // code is on the same line as the caret. + std::pair<FileID, unsigned> HintLocInfo + = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin()); + if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) { + // Insert the new code into the line just below the code + // that the user wrote. + unsigned HintColNo + = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second); + unsigned LastColumnModified + = HintColNo - 1 + I->CodeToInsert.size(); + if (LastColumnModified > FixItInsertionLine.size()) + FixItInsertionLine.resize(LastColumnModified, ' '); + std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(), + FixItInsertionLine.begin() + HintColNo - 1); + } else { + FixItInsertionLine.clear(); + break; + } + } + } + + if (FixItInsertionLine.empty()) + return FixItInsertionLine; + + // Now that we have the entire fixit line, expand the tabs in it. + // Since we don't want to insert spaces in the middle of a word, + // find each word and the column it should line up with and insert + // spaces until they match. + unsigned FixItPos = 0; + unsigned LinePos = 0; + unsigned TabExpandedCol = 0; + unsigned LineLength = LineEnd - LineStart; + + while (FixItPos < FixItInsertionLine.size() && LinePos < LineLength) { + // Find the next word in the FixIt line. + while (FixItPos < FixItInsertionLine.size() && + FixItInsertionLine[FixItPos] == ' ') + ++FixItPos; + unsigned CharDistance = FixItPos - TabExpandedCol; + + // Walk forward in the source line, keeping track of + // the tab-expanded column. + for (unsigned I = 0; I < CharDistance; ++I, ++LinePos) + if (LinePos >= LineLength || LineStart[LinePos] != '\t') + ++TabExpandedCol; + else + TabExpandedCol = + (TabExpandedCol/DiagOpts.TabStop + 1) * DiagOpts.TabStop; + + // Adjust the fixit line to match this column. + FixItInsertionLine.insert(FixItPos, TabExpandedCol-FixItPos, ' '); + FixItPos = TabExpandedCol; + + // Walk to the end of the word. + while (FixItPos < FixItInsertionLine.size() && + FixItInsertionLine[FixItPos] != ' ') + ++FixItPos; + } + + return FixItInsertionLine; +} + +void TextDiagnostic::expandTabs(std::string &SourceLine, + std::string &CaretLine) { + // Scan the source line, looking for tabs. If we find any, manually expand + // them to spaces and update the CaretLine to match. + for (unsigned i = 0; i != SourceLine.size(); ++i) { + if (SourceLine[i] != '\t') continue; + + // Replace this tab with at least one space. + SourceLine[i] = ' '; + + // Compute the number of spaces we need to insert. + unsigned TabStop = DiagOpts.TabStop; + assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop && + "Invalid -ftabstop value"); + unsigned NumSpaces = ((i+TabStop)/TabStop * TabStop) - (i+1); + assert(NumSpaces < TabStop && "Invalid computation of space amt"); + + // Insert spaces into the SourceLine. + SourceLine.insert(i+1, NumSpaces, ' '); + + // Insert spaces or ~'s into CaretLine. + CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' '); + } +} + +void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints) { + if (!DiagOpts.ShowParseableFixits) + return; + + // We follow FixItRewriter's example in not (yet) handling + // fix-its in macros. + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + if (I->RemoveRange.isInvalid() || + I->RemoveRange.getBegin().isMacroID() || + I->RemoveRange.getEnd().isMacroID()) + return; + } + + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + SourceLocation BLoc = I->RemoveRange.getBegin(); + SourceLocation ELoc = I->RemoveRange.getEnd(); + + std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc); + std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc); + + // Adjust for token ranges. + if (I->RemoveRange.isTokenRange()) + EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); + + // We specifically do not do word-wrapping or tab-expansion here, + // because this is supposed to be easy to parse. + PresumedLoc PLoc = SM.getPresumedLoc(BLoc); + if (PLoc.isInvalid()) + break; + + OS << "fix-it:\""; + OS.write_escaped(PLoc.getFilename()); + OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second) + << ':' << SM.getColumnNumber(BInfo.first, BInfo.second) + << '-' << SM.getLineNumber(EInfo.first, EInfo.second) + << ':' << SM.getColumnNumber(EInfo.first, EInfo.second) + << "}:\""; + OS.write_escaped(I->CodeToInsert); + OS << "\"\n"; + } +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp index f8ea9f1..57105f1 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp @@ -24,7 +24,7 @@ void TextDiagnosticBuffer::HandleDiagnostic(DiagnosticsEngine::Level Level, // Default implementation (Warnings/errors count). DiagnosticConsumer::HandleDiagnostic(Level, Info); - llvm::SmallString<100> Buf; + SmallString<100> Buf; Info.FormatDiagnostic(Buf); switch (Level) { default: llvm_unreachable( diff --git a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp index 10e7238..6445a0c 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp @@ -15,6 +15,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Frontend/TextDiagnostic.h" #include "clang/Lex/Lexer.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" @@ -23,28 +24,10 @@ #include <algorithm> using namespace clang; -static const enum raw_ostream::Colors noteColor = - raw_ostream::BLACK; -static const enum raw_ostream::Colors fixitColor = - raw_ostream::GREEN; -static const enum raw_ostream::Colors caretColor = - raw_ostream::GREEN; -static const enum raw_ostream::Colors warningColor = - raw_ostream::MAGENTA; -static const enum raw_ostream::Colors errorColor = raw_ostream::RED; -static const enum raw_ostream::Colors fatalColor = raw_ostream::RED; -// Used for changing only the bold attribute. -static const enum raw_ostream::Colors savedColor = - raw_ostream::SAVEDCOLOR; - -/// \brief Number of spaces to indent when word-wrapping. -const unsigned WordWrapIndentation = 6; - TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os, const DiagnosticOptions &diags, bool _OwnsOutputStream) - : OS(os), LangOpts(0), DiagOpts(&diags), - LastCaretDiagnosticWasNote(0), + : OS(os), LangOpts(0), DiagOpts(&diags), SM(0), OwnsOutputStream(_OwnsOutputStream) { } @@ -53,878 +36,14 @@ TextDiagnosticPrinter::~TextDiagnosticPrinter() { delete &OS; } -/// \brief Helper to recursivly walk up the include stack and print each layer -/// on the way back down. -static void PrintIncludeStackRecursively(raw_ostream &OS, - const SourceManager &SM, - SourceLocation Loc, - bool ShowLocation) { - if (Loc.isInvalid()) - return; - - PresumedLoc PLoc = SM.getPresumedLoc(Loc); - if (PLoc.isInvalid()) - return; - - // Print out the other include frames first. - PrintIncludeStackRecursively(OS, SM, PLoc.getIncludeLoc(), ShowLocation); - - if (ShowLocation) - OS << "In file included from " << PLoc.getFilename() - << ':' << PLoc.getLine() << ":\n"; - else - OS << "In included file:\n"; -} - -/// \brief Prints an include stack when appropriate for a particular diagnostic -/// level and location. -/// -/// This routine handles all the logic of suppressing particular include stacks -/// (such as those for notes) and duplicate include stacks when repeated -/// warnings occur within the same file. It also handles the logic of -/// customizing the formatting and display of the include stack. -/// -/// \param Level The diagnostic level of the message this stack pertains to. -/// \param Loc The include location of the current file (not the diagnostic -/// location). -void TextDiagnosticPrinter::PrintIncludeStack(DiagnosticsEngine::Level Level, - SourceLocation Loc, - const SourceManager &SM) { - // Skip redundant include stacks altogether. - if (LastWarningLoc == Loc) - return; - LastWarningLoc = Loc; - - if (!DiagOpts->ShowNoteIncludeStack && Level == DiagnosticsEngine::Note) - return; - - PrintIncludeStackRecursively(OS, SM, Loc, DiagOpts->ShowLocation); -} - -/// \brief When the source code line we want to print is too long for -/// the terminal, select the "interesting" region. -static void SelectInterestingSourceRegion(std::string &SourceLine, - std::string &CaretLine, - std::string &FixItInsertionLine, - unsigned EndOfCaretToken, - unsigned Columns) { - unsigned MaxSize = std::max(SourceLine.size(), - std::max(CaretLine.size(), - FixItInsertionLine.size())); - if (MaxSize > SourceLine.size()) - SourceLine.resize(MaxSize, ' '); - if (MaxSize > CaretLine.size()) - CaretLine.resize(MaxSize, ' '); - if (!FixItInsertionLine.empty() && MaxSize > FixItInsertionLine.size()) - FixItInsertionLine.resize(MaxSize, ' '); - - // Find the slice that we need to display the full caret line - // correctly. - unsigned CaretStart = 0, CaretEnd = CaretLine.size(); - for (; CaretStart != CaretEnd; ++CaretStart) - if (!isspace(CaretLine[CaretStart])) - break; - - for (; CaretEnd != CaretStart; --CaretEnd) - if (!isspace(CaretLine[CaretEnd - 1])) - break; - - // Make sure we don't chop the string shorter than the caret token - // itself. - if (CaretEnd < EndOfCaretToken) - CaretEnd = EndOfCaretToken; - - // If we have a fix-it line, make sure the slice includes all of the - // fix-it information. - if (!FixItInsertionLine.empty()) { - unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size(); - for (; FixItStart != FixItEnd; ++FixItStart) - if (!isspace(FixItInsertionLine[FixItStart])) - break; - - for (; FixItEnd != FixItStart; --FixItEnd) - if (!isspace(FixItInsertionLine[FixItEnd - 1])) - break; - - if (FixItStart < CaretStart) - CaretStart = FixItStart; - if (FixItEnd > CaretEnd) - CaretEnd = FixItEnd; - } - - // CaretLine[CaretStart, CaretEnd) contains all of the interesting - // parts of the caret line. While this slice is smaller than the - // number of columns we have, try to grow the slice to encompass - // more context. - - // If the end of the interesting region comes before we run out of - // space in the terminal, start at the beginning of the line. - if (Columns > 3 && CaretEnd < Columns - 3) - CaretStart = 0; - - unsigned TargetColumns = Columns; - if (TargetColumns > 8) - TargetColumns -= 8; // Give us extra room for the ellipses. - unsigned SourceLength = SourceLine.size(); - while ((CaretEnd - CaretStart) < TargetColumns) { - bool ExpandedRegion = false; - // Move the start of the interesting region left until we've - // pulled in something else interesting. - if (CaretStart == 1) - CaretStart = 0; - else if (CaretStart > 1) { - unsigned NewStart = CaretStart - 1; - - // Skip over any whitespace we see here; we're looking for - // another bit of interesting text. - while (NewStart && isspace(SourceLine[NewStart])) - --NewStart; - - // Skip over this bit of "interesting" text. - while (NewStart && !isspace(SourceLine[NewStart])) - --NewStart; - - // Move up to the non-whitespace character we just saw. - if (NewStart) - ++NewStart; - - // If we're still within our limit, update the starting - // position within the source/caret line. - if (CaretEnd - NewStart <= TargetColumns) { - CaretStart = NewStart; - ExpandedRegion = true; - } - } - - // Move the end of the interesting region right until we've - // pulled in something else interesting. - if (CaretEnd != SourceLength) { - assert(CaretEnd < SourceLength && "Unexpected caret position!"); - unsigned NewEnd = CaretEnd; - - // Skip over any whitespace we see here; we're looking for - // another bit of interesting text. - while (NewEnd != SourceLength && isspace(SourceLine[NewEnd - 1])) - ++NewEnd; - - // Skip over this bit of "interesting" text. - while (NewEnd != SourceLength && !isspace(SourceLine[NewEnd - 1])) - ++NewEnd; - - if (NewEnd - CaretStart <= TargetColumns) { - CaretEnd = NewEnd; - ExpandedRegion = true; - } - } - - if (!ExpandedRegion) - break; - } - - // [CaretStart, CaretEnd) is the slice we want. Update the various - // output lines to show only this slice, with two-space padding - // before the lines so that it looks nicer. - if (CaretEnd < SourceLine.size()) - SourceLine.replace(CaretEnd, std::string::npos, "..."); - if (CaretEnd < CaretLine.size()) - CaretLine.erase(CaretEnd, std::string::npos); - if (FixItInsertionLine.size() > CaretEnd) - FixItInsertionLine.erase(CaretEnd, std::string::npos); - - if (CaretStart > 2) { - SourceLine.replace(0, CaretStart, " ..."); - CaretLine.replace(0, CaretStart, " "); - if (FixItInsertionLine.size() >= CaretStart) - FixItInsertionLine.replace(0, CaretStart, " "); - } -} - -/// Look through spelling locations for a macro argument expansion, and -/// if found skip to it so that we can trace the argument rather than the macros -/// in which that argument is used. If no macro argument expansion is found, -/// don't skip anything and return the starting location. -static SourceLocation skipToMacroArgExpansion(const SourceManager &SM, - SourceLocation StartLoc) { - for (SourceLocation L = StartLoc; L.isMacroID(); - L = SM.getImmediateSpellingLoc(L)) { - if (SM.isMacroArgExpansion(L)) - return L; - } - - // Otherwise just return initial location, there's nothing to skip. - return StartLoc; -} - -/// Gets the location of the immediate macro caller, one level up the stack -/// toward the initial macro typed into the source. -static SourceLocation getImmediateMacroCallerLoc(const SourceManager &SM, - SourceLocation Loc) { - if (!Loc.isMacroID()) return Loc; - - // When we have the location of (part of) an expanded parameter, its spelling - // location points to the argument as typed into the macro call, and - // therefore is used to locate the macro caller. - if (SM.isMacroArgExpansion(Loc)) - return SM.getImmediateSpellingLoc(Loc); - - // Otherwise, the caller of the macro is located where this macro is - // expanded (while the spelling is part of the macro definition). - return SM.getImmediateExpansionRange(Loc).first; -} - -/// Gets the location of the immediate macro callee, one level down the stack -/// toward the leaf macro. -static SourceLocation getImmediateMacroCalleeLoc(const SourceManager &SM, - SourceLocation Loc) { - if (!Loc.isMacroID()) return Loc; - - // When we have the location of (part of) an expanded parameter, its - // expansion location points to the unexpanded paramater reference within - // the macro definition (or callee). - if (SM.isMacroArgExpansion(Loc)) - return SM.getImmediateExpansionRange(Loc).first; - - // Otherwise, the callee of the macro is located where this location was - // spelled inside the macro definition. - return SM.getImmediateSpellingLoc(Loc); -} - -namespace { - -/// \brief Class to encapsulate the logic for formatting and printing a textual -/// diagnostic message. -/// -/// This class provides an interface for building and emitting a textual -/// diagnostic, including all of the macro backtraces, caret diagnostics, FixIt -/// Hints, and code snippets. In the presence of macros this involves -/// a recursive process, synthesizing notes for each macro expansion. -/// -/// The purpose of this class is to isolate the implementation of printing -/// beautiful text diagnostics from any particular interfaces. The Clang -/// DiagnosticClient is implemented through this class as is diagnostic -/// printing coming out of libclang. -/// -/// A brief worklist: -/// FIXME: Sink the printing of the diagnostic message itself into this class. -/// FIXME: Sink the printing of the include stack into this class. -/// FIXME: Remove the TextDiagnosticPrinter as an input. -/// FIXME: Sink the recursive printing of template instantiations into this -/// class. -class TextDiagnostic { - TextDiagnosticPrinter &Printer; - raw_ostream &OS; - const SourceManager &SM; - const LangOptions &LangOpts; - const DiagnosticOptions &DiagOpts; - -public: - TextDiagnostic(TextDiagnosticPrinter &Printer, - raw_ostream &OS, - const SourceManager &SM, - const LangOptions &LangOpts, - const DiagnosticOptions &DiagOpts) - : Printer(Printer), OS(OS), SM(SM), LangOpts(LangOpts), DiagOpts(DiagOpts) { - } - - /// \brief Emit the caret and underlining text. - /// - /// Walks up the macro expansion stack printing the code snippet, caret, - /// underlines and FixItHint display as appropriate at each level. Walk is - /// accomplished by calling itself recursively. - /// - /// FIXME: Remove macro expansion from this routine, it shouldn't be tied to - /// caret diagnostics. - /// FIXME: Break up massive function into logical units. - /// - /// \param Loc The location for this caret. - /// \param Ranges The underlined ranges for this code snippet. - /// \param Hints The FixIt hints active for this diagnostic. - /// \param MacroSkipEnd The depth to stop skipping macro expansions. - /// \param OnMacroInst The current depth of the macro expansion stack. - void EmitCaret(SourceLocation Loc, - SmallVectorImpl<CharSourceRange>& Ranges, - ArrayRef<FixItHint> Hints, - unsigned &MacroDepth, - unsigned OnMacroInst = 0) { - assert(!Loc.isInvalid() && "must have a valid source location here"); - - // If this is a file source location, directly emit the source snippet and - // caret line. Also record the macro depth reached. - if (Loc.isFileID()) { - assert(MacroDepth == 0 && "We shouldn't hit a leaf node twice!"); - MacroDepth = OnMacroInst; - EmitSnippetAndCaret(Loc, Ranges, Hints); - return; - } - // Otherwise recurse through each macro expansion layer. - - // When processing macros, skip over the expansions leading up to - // a macro argument, and trace the argument's expansion stack instead. - Loc = skipToMacroArgExpansion(SM, Loc); - - SourceLocation OneLevelUp = getImmediateMacroCallerLoc(SM, Loc); - - // FIXME: Map ranges? - EmitCaret(OneLevelUp, Ranges, Hints, MacroDepth, OnMacroInst + 1); - - // Map the location. - Loc = getImmediateMacroCalleeLoc(SM, Loc); - - unsigned MacroSkipStart = 0, MacroSkipEnd = 0; - if (MacroDepth > DiagOpts.MacroBacktraceLimit) { - MacroSkipStart = DiagOpts.MacroBacktraceLimit / 2 + - DiagOpts.MacroBacktraceLimit % 2; - MacroSkipEnd = MacroDepth - DiagOpts.MacroBacktraceLimit / 2; - } - - // Whether to suppress printing this macro expansion. - bool Suppressed = (OnMacroInst >= MacroSkipStart && - OnMacroInst < MacroSkipEnd); - - // Map the ranges. - for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), - E = Ranges.end(); - I != E; ++I) { - SourceLocation Start = I->getBegin(), End = I->getEnd(); - if (Start.isMacroID()) - I->setBegin(getImmediateMacroCalleeLoc(SM, Start)); - if (End.isMacroID()) - I->setEnd(getImmediateMacroCalleeLoc(SM, End)); - } - - if (!Suppressed) { - // Don't print recursive expansion notes from an expansion note. - Loc = SM.getSpellingLoc(Loc); - - // Get the pretty name, according to #line directives etc. - PresumedLoc PLoc = SM.getPresumedLoc(Loc); - if (PLoc.isInvalid()) - return; - - // If this diagnostic is not in the main file, print out the - // "included from" lines. - Printer.PrintIncludeStack(DiagnosticsEngine::Note, PLoc.getIncludeLoc(), - SM); - - if (DiagOpts.ShowLocation) { - // Emit the file/line/column that this expansion came from. - OS << PLoc.getFilename() << ':' << PLoc.getLine() << ':'; - if (DiagOpts.ShowColumn) - OS << PLoc.getColumn() << ':'; - OS << ' '; - } - OS << "note: expanded from:\n"; - - EmitSnippetAndCaret(Loc, Ranges, ArrayRef<FixItHint>()); - return; - } - - if (OnMacroInst == MacroSkipStart) { - // Tell the user that we've skipped contexts. - OS << "note: (skipping " << (MacroSkipEnd - MacroSkipStart) - << " expansions in backtrace; use -fmacro-backtrace-limit=0 to see " - "all)\n"; - } - } - - /// \brief Emit a code snippet and caret line. - /// - /// This routine emits a single line's code snippet and caret line.. - /// - /// \param Loc The location for the caret. - /// \param Ranges The underlined ranges for this code snippet. - /// \param Hints The FixIt hints active for this diagnostic. - void EmitSnippetAndCaret(SourceLocation Loc, - SmallVectorImpl<CharSourceRange>& Ranges, - ArrayRef<FixItHint> Hints) { - assert(!Loc.isInvalid() && "must have a valid source location here"); - assert(Loc.isFileID() && "must have a file location here"); - - // Decompose the location into a FID/Offset pair. - std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); - FileID FID = LocInfo.first; - unsigned FileOffset = LocInfo.second; - - // Get information about the buffer it points into. - bool Invalid = false; - const char *BufStart = SM.getBufferData(FID, &Invalid).data(); - if (Invalid) - return; - - unsigned LineNo = SM.getLineNumber(FID, FileOffset); - unsigned ColNo = SM.getColumnNumber(FID, FileOffset); - unsigned CaretEndColNo - = ColNo + Lexer::MeasureTokenLength(Loc, SM, LangOpts); - - // Rewind from the current position to the start of the line. - const char *TokPtr = BufStart+FileOffset; - const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. - - - // Compute the line end. Scan forward from the error position to the end of - // the line. - const char *LineEnd = TokPtr; - while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') - ++LineEnd; - - // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past - // the source line length as currently being computed. See - // test/Misc/message-length.c. - CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart)); - - // Copy the line of code into an std::string for ease of manipulation. - std::string SourceLine(LineStart, LineEnd); - - // Create a line for the caret that is filled with spaces that is the same - // length as the line of source code. - std::string CaretLine(LineEnd-LineStart, ' '); - - // Highlight all of the characters covered by Ranges with ~ characters. - for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), - E = Ranges.end(); - I != E; ++I) - HighlightRange(*I, LineNo, FID, SourceLine, CaretLine); - - // Next, insert the caret itself. - if (ColNo-1 < CaretLine.size()) - CaretLine[ColNo-1] = '^'; - else - CaretLine.push_back('^'); - - ExpandTabs(SourceLine, CaretLine); - - // If we are in -fdiagnostics-print-source-range-info mode, we are trying - // to produce easily machine parsable output. Add a space before the - // source line and the caret to make it trivial to tell the main diagnostic - // line from what the user is intended to see. - if (DiagOpts.ShowSourceRanges) { - SourceLine = ' ' + SourceLine; - CaretLine = ' ' + CaretLine; - } - - std::string FixItInsertionLine = BuildFixItInsertionLine(LineNo, - LineStart, LineEnd, - Hints); - - // If the source line is too long for our terminal, select only the - // "interesting" source region within that line. - unsigned Columns = DiagOpts.MessageLength; - if (Columns && SourceLine.size() > Columns) - SelectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine, - CaretEndColNo, Columns); - - // Finally, remove any blank spaces from the end of CaretLine. - while (CaretLine[CaretLine.size()-1] == ' ') - CaretLine.erase(CaretLine.end()-1); - - // Emit what we have computed. - OS << SourceLine << '\n'; - - if (DiagOpts.ShowColors) - OS.changeColor(caretColor, true); - OS << CaretLine << '\n'; - if (DiagOpts.ShowColors) - OS.resetColor(); - - if (!FixItInsertionLine.empty()) { - if (DiagOpts.ShowColors) - // Print fixit line in color - OS.changeColor(fixitColor, false); - if (DiagOpts.ShowSourceRanges) - OS << ' '; - OS << FixItInsertionLine << '\n'; - if (DiagOpts.ShowColors) - OS.resetColor(); - } - - // Print out any parseable fixit information requested by the options. - EmitParseableFixits(Hints); - } - -private: - /// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo. - void HighlightRange(const CharSourceRange &R, - unsigned LineNo, FileID FID, - const std::string &SourceLine, - std::string &CaretLine) { - assert(CaretLine.size() == SourceLine.size() && - "Expect a correspondence between source and caret line!"); - if (!R.isValid()) return; - - SourceLocation Begin = SM.getExpansionLoc(R.getBegin()); - SourceLocation End = SM.getExpansionLoc(R.getEnd()); - - // If the End location and the start location are the same and are a macro - // location, then the range was something that came from a macro expansion - // or _Pragma. If this is an object-like macro, the best we can do is to - // highlight the range. If this is a function-like macro, we'd also like to - // highlight the arguments. - if (Begin == End && R.getEnd().isMacroID()) - End = SM.getExpansionRange(R.getEnd()).second; - - unsigned StartLineNo = SM.getExpansionLineNumber(Begin); - if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) - return; // No intersection. - - unsigned EndLineNo = SM.getExpansionLineNumber(End); - if (EndLineNo < LineNo || SM.getFileID(End) != FID) - return; // No intersection. - - // Compute the column number of the start. - unsigned StartColNo = 0; - if (StartLineNo == LineNo) { - StartColNo = SM.getExpansionColumnNumber(Begin); - if (StartColNo) --StartColNo; // Zero base the col #. - } - - // Compute the column number of the end. - unsigned EndColNo = CaretLine.size(); - if (EndLineNo == LineNo) { - EndColNo = SM.getExpansionColumnNumber(End); - if (EndColNo) { - --EndColNo; // Zero base the col #. - - // Add in the length of the token, so that we cover multi-char tokens if - // this is a token range. - if (R.isTokenRange()) - EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts); - } else { - EndColNo = CaretLine.size(); - } - } - - assert(StartColNo <= EndColNo && "Invalid range!"); - - // Check that a token range does not highlight only whitespace. - if (R.isTokenRange()) { - // Pick the first non-whitespace column. - while (StartColNo < SourceLine.size() && - (SourceLine[StartColNo] == ' ' || SourceLine[StartColNo] == '\t')) - ++StartColNo; - - // Pick the last non-whitespace column. - if (EndColNo > SourceLine.size()) - EndColNo = SourceLine.size(); - while (EndColNo-1 && - (SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t')) - --EndColNo; - - // If the start/end passed each other, then we are trying to highlight a - // range that just exists in whitespace, which must be some sort of other - // bug. - assert(StartColNo <= EndColNo && "Trying to highlight whitespace??"); - } - - // Fill the range with ~'s. - for (unsigned i = StartColNo; i < EndColNo; ++i) - CaretLine[i] = '~'; - } - - std::string BuildFixItInsertionLine(unsigned LineNo, - const char *LineStart, - const char *LineEnd, - ArrayRef<FixItHint> Hints) { - std::string FixItInsertionLine; - if (Hints.empty() || !DiagOpts.ShowFixits) - return FixItInsertionLine; - - for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); - I != E; ++I) { - if (!I->CodeToInsert.empty()) { - // We have an insertion hint. Determine whether the inserted - // code is on the same line as the caret. - std::pair<FileID, unsigned> HintLocInfo - = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin()); - if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) { - // Insert the new code into the line just below the code - // that the user wrote. - unsigned HintColNo - = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second); - unsigned LastColumnModified - = HintColNo - 1 + I->CodeToInsert.size(); - if (LastColumnModified > FixItInsertionLine.size()) - FixItInsertionLine.resize(LastColumnModified, ' '); - std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(), - FixItInsertionLine.begin() + HintColNo - 1); - } else { - FixItInsertionLine.clear(); - break; - } - } - } - - if (FixItInsertionLine.empty()) - return FixItInsertionLine; - - // Now that we have the entire fixit line, expand the tabs in it. - // Since we don't want to insert spaces in the middle of a word, - // find each word and the column it should line up with and insert - // spaces until they match. - unsigned FixItPos = 0; - unsigned LinePos = 0; - unsigned TabExpandedCol = 0; - unsigned LineLength = LineEnd - LineStart; - - while (FixItPos < FixItInsertionLine.size() && LinePos < LineLength) { - // Find the next word in the FixIt line. - while (FixItPos < FixItInsertionLine.size() && - FixItInsertionLine[FixItPos] == ' ') - ++FixItPos; - unsigned CharDistance = FixItPos - TabExpandedCol; - - // Walk forward in the source line, keeping track of - // the tab-expanded column. - for (unsigned I = 0; I < CharDistance; ++I, ++LinePos) - if (LinePos >= LineLength || LineStart[LinePos] != '\t') - ++TabExpandedCol; - else - TabExpandedCol = - (TabExpandedCol/DiagOpts.TabStop + 1) * DiagOpts.TabStop; - - // Adjust the fixit line to match this column. - FixItInsertionLine.insert(FixItPos, TabExpandedCol-FixItPos, ' '); - FixItPos = TabExpandedCol; - - // Walk to the end of the word. - while (FixItPos < FixItInsertionLine.size() && - FixItInsertionLine[FixItPos] != ' ') - ++FixItPos; - } - - return FixItInsertionLine; - } - - void ExpandTabs(std::string &SourceLine, std::string &CaretLine) { - // Scan the source line, looking for tabs. If we find any, manually expand - // them to spaces and update the CaretLine to match. - for (unsigned i = 0; i != SourceLine.size(); ++i) { - if (SourceLine[i] != '\t') continue; - - // Replace this tab with at least one space. - SourceLine[i] = ' '; - - // Compute the number of spaces we need to insert. - unsigned TabStop = DiagOpts.TabStop; - assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop && - "Invalid -ftabstop value"); - unsigned NumSpaces = ((i+TabStop)/TabStop * TabStop) - (i+1); - assert(NumSpaces < TabStop && "Invalid computation of space amt"); - - // Insert spaces into the SourceLine. - SourceLine.insert(i+1, NumSpaces, ' '); - - // Insert spaces or ~'s into CaretLine. - CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' '); - } - } - - void EmitParseableFixits(ArrayRef<FixItHint> Hints) { - if (!DiagOpts.ShowParseableFixits) - return; - - // We follow FixItRewriter's example in not (yet) handling - // fix-its in macros. - for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); - I != E; ++I) { - if (I->RemoveRange.isInvalid() || - I->RemoveRange.getBegin().isMacroID() || - I->RemoveRange.getEnd().isMacroID()) - return; - } - - for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); - I != E; ++I) { - SourceLocation BLoc = I->RemoveRange.getBegin(); - SourceLocation ELoc = I->RemoveRange.getEnd(); - - std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc); - std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc); - - // Adjust for token ranges. - if (I->RemoveRange.isTokenRange()) - EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); - - // We specifically do not do word-wrapping or tab-expansion here, - // because this is supposed to be easy to parse. - PresumedLoc PLoc = SM.getPresumedLoc(BLoc); - if (PLoc.isInvalid()) - break; - - OS << "fix-it:\""; - OS.write_escaped(PLoc.getFilename()); - OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second) - << ':' << SM.getColumnNumber(BInfo.first, BInfo.second) - << '-' << SM.getLineNumber(EInfo.first, EInfo.second) - << ':' << SM.getColumnNumber(EInfo.first, EInfo.second) - << "}:\""; - OS.write_escaped(I->CodeToInsert); - OS << "\"\n"; - } - } -}; - -} // end namespace - -/// Get the presumed location of a diagnostic message. This computes the -/// presumed location for the top of any macro backtrace when present. -static PresumedLoc getDiagnosticPresumedLoc(const SourceManager &SM, - SourceLocation Loc) { - // This is a condensed form of the algorithm used by EmitCaretDiagnostic to - // walk to the top of the macro call stack. - while (Loc.isMacroID()) { - Loc = skipToMacroArgExpansion(SM, Loc); - Loc = getImmediateMacroCallerLoc(SM, Loc); - } - - return SM.getPresumedLoc(Loc); -} - -/// \brief Print out the file/line/column information and include trace. -/// -/// This method handlen the emission of the diagnostic location information. -/// This includes extracting as much location information as is present for the -/// diagnostic and printing it, as well as any include stack or source ranges -/// necessary. -void TextDiagnosticPrinter::EmitDiagnosticLoc(DiagnosticsEngine::Level Level, - const Diagnostic &Info, - const SourceManager &SM, - PresumedLoc PLoc) { - if (PLoc.isInvalid()) { - // At least print the file name if available: - FileID FID = SM.getFileID(Info.getLocation()); - if (!FID.isInvalid()) { - const FileEntry* FE = SM.getFileEntryForID(FID); - if (FE && FE->getName()) { - OS << FE->getName(); - if (FE->getDevice() == 0 && FE->getInode() == 0 - && FE->getFileMode() == 0) { - // in PCH is a guess, but a good one: - OS << " (in PCH)"; - } - OS << ": "; - } - } - return; - } - unsigned LineNo = PLoc.getLine(); - - if (!DiagOpts->ShowLocation) - return; - - if (DiagOpts->ShowColors) - OS.changeColor(savedColor, true); - - OS << PLoc.getFilename(); - switch (DiagOpts->Format) { - case DiagnosticOptions::Clang: OS << ':' << LineNo; break; - case DiagnosticOptions::Msvc: OS << '(' << LineNo; break; - case DiagnosticOptions::Vi: OS << " +" << LineNo; break; - } - - if (DiagOpts->ShowColumn) - // Compute the column number. - if (unsigned ColNo = PLoc.getColumn()) { - if (DiagOpts->Format == DiagnosticOptions::Msvc) { - OS << ','; - ColNo--; - } else - OS << ':'; - OS << ColNo; - } - switch (DiagOpts->Format) { - case DiagnosticOptions::Clang: - case DiagnosticOptions::Vi: OS << ':'; break; - case DiagnosticOptions::Msvc: OS << ") : "; break; - } - - if (DiagOpts->ShowSourceRanges && Info.getNumRanges()) { - FileID CaretFileID = - SM.getFileID(SM.getExpansionLoc(Info.getLocation())); - bool PrintedRange = false; - - for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) { - // Ignore invalid ranges. - if (!Info.getRange(i).isValid()) continue; - - SourceLocation B = Info.getRange(i).getBegin(); - SourceLocation E = Info.getRange(i).getEnd(); - B = SM.getExpansionLoc(B); - E = SM.getExpansionLoc(E); - - // If the End location and the start location are the same and are a - // macro location, then the range was something that came from a - // macro expansion or _Pragma. If this is an object-like macro, the - // best we can do is to highlight the range. If this is a - // function-like macro, we'd also like to highlight the arguments. - if (B == E && Info.getRange(i).getEnd().isMacroID()) - E = SM.getExpansionRange(Info.getRange(i).getEnd()).second; - - std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); - std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); - - // If the start or end of the range is in another file, just discard - // it. - if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) - continue; - - // Add in the length of the token, so that we cover multi-char - // tokens. - unsigned TokSize = 0; - if (Info.getRange(i).isTokenRange()) - TokSize = Lexer::MeasureTokenLength(E, SM, *LangOpts); - - OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':' - << SM.getColumnNumber(BInfo.first, BInfo.second) << '-' - << SM.getLineNumber(EInfo.first, EInfo.second) << ':' - << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) - << '}'; - PrintedRange = true; - } - - if (PrintedRange) - OS << ':'; - } - OS << ' '; -} - -/// \brief Print the diagonstic level to a raw_ostream. -/// -/// Handles colorizing the level and formatting. -static void printDiagnosticLevel(raw_ostream &OS, - DiagnosticsEngine::Level Level, - bool ShowColors) { - if (ShowColors) { - // Print diagnostic category in bold and color - switch (Level) { - case DiagnosticsEngine::Ignored: - llvm_unreachable("Invalid diagnostic type"); - case DiagnosticsEngine::Note: OS.changeColor(noteColor, true); break; - case DiagnosticsEngine::Warning: OS.changeColor(warningColor, true); break; - case DiagnosticsEngine::Error: OS.changeColor(errorColor, true); break; - case DiagnosticsEngine::Fatal: OS.changeColor(fatalColor, true); break; - } - } - - switch (Level) { - case DiagnosticsEngine::Ignored: llvm_unreachable("Invalid diagnostic type"); - case DiagnosticsEngine::Note: OS << "note: "; break; - case DiagnosticsEngine::Warning: OS << "warning: "; break; - case DiagnosticsEngine::Error: OS << "error: "; break; - case DiagnosticsEngine::Fatal: OS << "fatal error: "; break; - } - - if (ShowColors) - OS.resetColor(); +void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO, + const Preprocessor *PP) { + LangOpts = &LO; } -/// \brief Print the diagnostic name to a raw_ostream. -/// -/// This prints the diagnostic name to a raw_ostream if it has one. It formats -/// the name according to the expected diagnostic message formatting: -/// " [diagnostic_name_here]" -static void printDiagnosticName(raw_ostream &OS, const Diagnostic &Info) { - if (!DiagnosticIDs::isBuiltinNote(Info.getID())) - OS << " [" << DiagnosticIDs::getName(Info.getID()) << "]"; +void TextDiagnosticPrinter::EndSourceFile() { + LangOpts = 0; + TextDiag.reset(0); } /// \brief Print any diagnostic option information to a raw_ostream. @@ -996,182 +115,6 @@ static void printDiagnosticOptions(raw_ostream &OS, OS << ']'; } -/// \brief Skip over whitespace in the string, starting at the given -/// index. -/// -/// \returns The index of the first non-whitespace character that is -/// greater than or equal to Idx or, if no such character exists, -/// returns the end of the string. -static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) { - while (Idx < Length && isspace(Str[Idx])) - ++Idx; - return Idx; -} - -/// \brief If the given character is the start of some kind of -/// balanced punctuation (e.g., quotes or parentheses), return the -/// character that will terminate the punctuation. -/// -/// \returns The ending punctuation character, if any, or the NULL -/// character if the input character does not start any punctuation. -static inline char findMatchingPunctuation(char c) { - switch (c) { - case '\'': return '\''; - case '`': return '\''; - case '"': return '"'; - case '(': return ')'; - case '[': return ']'; - case '{': return '}'; - default: break; - } - - return 0; -} - -/// \brief Find the end of the word starting at the given offset -/// within a string. -/// -/// \returns the index pointing one character past the end of the -/// word. -static unsigned findEndOfWord(unsigned Start, StringRef Str, - unsigned Length, unsigned Column, - unsigned Columns) { - assert(Start < Str.size() && "Invalid start position!"); - unsigned End = Start + 1; - - // If we are already at the end of the string, take that as the word. - if (End == Str.size()) - return End; - - // Determine if the start of the string is actually opening - // punctuation, e.g., a quote or parentheses. - char EndPunct = findMatchingPunctuation(Str[Start]); - if (!EndPunct) { - // This is a normal word. Just find the first space character. - while (End < Length && !isspace(Str[End])) - ++End; - return End; - } - - // We have the start of a balanced punctuation sequence (quotes, - // parentheses, etc.). Determine the full sequence is. - llvm::SmallString<16> PunctuationEndStack; - PunctuationEndStack.push_back(EndPunct); - while (End < Length && !PunctuationEndStack.empty()) { - if (Str[End] == PunctuationEndStack.back()) - PunctuationEndStack.pop_back(); - else if (char SubEndPunct = findMatchingPunctuation(Str[End])) - PunctuationEndStack.push_back(SubEndPunct); - - ++End; - } - - // Find the first space character after the punctuation ended. - while (End < Length && !isspace(Str[End])) - ++End; - - unsigned PunctWordLength = End - Start; - if (// If the word fits on this line - Column + PunctWordLength <= Columns || - // ... or the word is "short enough" to take up the next line - // without too much ugly white space - PunctWordLength < Columns/3) - return End; // Take the whole thing as a single "word". - - // The whole quoted/parenthesized string is too long to print as a - // single "word". Instead, find the "word" that starts just after - // the punctuation and use that end-point instead. This will recurse - // until it finds something small enough to consider a word. - return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns); -} - -/// \brief Print the given string to a stream, word-wrapping it to -/// some number of columns in the process. -/// -/// \param OS the stream to which the word-wrapping string will be -/// emitted. -/// \param Str the string to word-wrap and output. -/// \param Columns the number of columns to word-wrap to. -/// \param Column the column number at which the first character of \p -/// Str will be printed. This will be non-zero when part of the first -/// line has already been printed. -/// \param Indentation the number of spaces to indent any lines beyond -/// the first line. -/// \returns true if word-wrapping was required, or false if the -/// string fit on the first line. -static bool printWordWrapped(raw_ostream &OS, StringRef Str, - unsigned Columns, - unsigned Column = 0, - unsigned Indentation = WordWrapIndentation) { - const unsigned Length = std::min(Str.find('\n'), Str.size()); - - // The string used to indent each line. - llvm::SmallString<16> IndentStr; - IndentStr.assign(Indentation, ' '); - bool Wrapped = false; - for (unsigned WordStart = 0, WordEnd; WordStart < Length; - WordStart = WordEnd) { - // Find the beginning of the next word. - WordStart = skipWhitespace(WordStart, Str, Length); - if (WordStart == Length) - break; - - // Find the end of this word. - WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns); - - // Does this word fit on the current line? - unsigned WordLength = WordEnd - WordStart; - if (Column + WordLength < Columns) { - // This word fits on the current line; print it there. - if (WordStart) { - OS << ' '; - Column += 1; - } - OS << Str.substr(WordStart, WordLength); - Column += WordLength; - continue; - } - - // This word does not fit on the current line, so wrap to the next - // line. - OS << '\n'; - OS.write(&IndentStr[0], Indentation); - OS << Str.substr(WordStart, WordLength); - Column = Indentation + WordLength; - Wrapped = true; - } - - // Append any remaning text from the message with its existing formatting. - OS << Str.substr(Length); - - return Wrapped; -} - -static void printDiagnosticMessage(raw_ostream &OS, - DiagnosticsEngine::Level Level, - StringRef Message, - unsigned CurrentColumn, unsigned Columns, - bool ShowColors) { - if (ShowColors) { - // Print warnings, errors and fatal errors in bold, no color - switch (Level) { - case DiagnosticsEngine::Warning: OS.changeColor(savedColor, true); break; - case DiagnosticsEngine::Error: OS.changeColor(savedColor, true); break; - case DiagnosticsEngine::Fatal: OS.changeColor(savedColor, true); break; - default: break; //don't bold notes - } - } - - if (Columns) - printWordWrapped(OS, Message, Columns, CurrentColumn); - else - OS << Message; - - if (ShowColors) - OS.resetColor(); - OS << '\n'; -} - void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, const Diagnostic &Info) { // Default implementation (Warnings/errors count). @@ -1179,15 +122,12 @@ void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, // Render the diagnostic message into a temporary buffer eagerly. We'll use // this later as we print out the diagnostic to the terminal. - llvm::SmallString<100> OutStr; + SmallString<100> OutStr; Info.FormatDiagnostic(OutStr); llvm::raw_svector_ostream DiagMessageStream(OutStr); - if (DiagOpts->ShowNames) - printDiagnosticName(DiagMessageStream, Info); printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts); - // Keeps track of the the starting position of the location // information (e.g., "foo.c:10:4:") that precedes the error // message. We use this information to determine how long the @@ -1202,10 +142,11 @@ void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, // diagnostics in a context that lacks language options, a source manager, or // other infrastructure necessary when emitting more rich diagnostics. if (!Info.getLocation().isValid()) { - printDiagnosticLevel(OS, Level, DiagOpts->ShowColors); - printDiagnosticMessage(OS, Level, DiagMessageStream.str(), - OS.tell() - StartOfLocationInfo, - DiagOpts->MessageLength, DiagOpts->ShowColors); + TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors); + TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(), + OS.tell() - StartOfLocationInfo, + DiagOpts->MessageLength, + DiagOpts->ShowColors); OS.flush(); return; } @@ -1215,60 +156,19 @@ void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, assert(DiagOpts && "Unexpected diagnostic without options set"); assert(Info.hasSourceManager() && "Unexpected diagnostic with no source manager"); - const SourceManager &SM = Info.getSourceManager(); - TextDiagnostic TextDiag(*this, OS, SM, *LangOpts, *DiagOpts); - - PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Info.getLocation()); - - // First, if this diagnostic is not in the main file, print out the - // "included from" lines. - PrintIncludeStack(Level, PLoc.getIncludeLoc(), SM); - StartOfLocationInfo = OS.tell(); - - // Next emit the location of this particular diagnostic. - EmitDiagnosticLoc(Level, Info, SM, PLoc); - if (DiagOpts->ShowColors) - OS.resetColor(); - - printDiagnosticLevel(OS, Level, DiagOpts->ShowColors); - printDiagnosticMessage(OS, Level, DiagMessageStream.str(), - OS.tell() - StartOfLocationInfo, - DiagOpts->MessageLength, DiagOpts->ShowColors); - - // If caret diagnostics are enabled and we have location, we want to - // emit the caret. However, we only do this if the location moved - // from the last diagnostic, if the last diagnostic was a note that - // was part of a different warning or error diagnostic, or if the - // diagnostic has ranges. We don't want to emit the same caret - // multiple times if one loc has multiple diagnostics. - if (DiagOpts->ShowCarets && - ((LastLoc != Info.getLocation()) || Info.getNumRanges() || - (LastCaretDiagnosticWasNote && Level != DiagnosticsEngine::Note) || - Info.getNumFixItHints())) { - // Cache the LastLoc, it allows us to omit duplicate source/caret spewage. - LastLoc = FullSourceLoc(Info.getLocation(), Info.getSourceManager()); - LastCaretDiagnosticWasNote = (Level == DiagnosticsEngine::Note); - - // Get the ranges into a local array we can hack on. - SmallVector<CharSourceRange, 20> Ranges; - Ranges.reserve(Info.getNumRanges()); - for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) - Ranges.push_back(Info.getRange(i)); - - for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i) { - const FixItHint &Hint = Info.getFixItHint(i); - if (Hint.RemoveRange.isValid()) - Ranges.push_back(Hint.RemoveRange); - } - - unsigned MacroDepth = 0; - TextDiag.EmitCaret(LastLoc, Ranges, - llvm::makeArrayRef(Info.getFixItHints(), - Info.getNumFixItHints()), - MacroDepth); + // Rebuild the TextDiagnostic utility if missing or the source manager has + // changed. + if (!TextDiag || SM != &Info.getSourceManager()) { + SM = &Info.getSourceManager(); + TextDiag.reset(new TextDiagnostic(OS, *SM, *LangOpts, *DiagOpts)); } + TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(), + Info.getRanges(), + llvm::makeArrayRef(Info.getFixItHints(), + Info.getNumFixItHints())); + OS.flush(); } diff --git a/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp index cf35c8e..552282d 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -18,6 +18,8 @@ #include "llvm/ADT/SmallString.h" #include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" +#include <climits> + using namespace clang; VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags) @@ -38,7 +40,7 @@ VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { // DiagnosticConsumer interface. void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, - const Preprocessor *PP) { + const Preprocessor *PP) { // FIXME: Const hack, we screw up the preprocessor but in practice its ok // because it doesn't get reused. It would be better if we could make a copy // though. @@ -82,6 +84,9 @@ public: static Directive* Create(bool RegexKind, const SourceLocation &Location, const std::string &Text, unsigned Count); public: + /// Constant representing one or more matches aka regex "+". + static const unsigned OneOrMoreCount = UINT_MAX; + SourceLocation Location; const std::string Text; unsigned Count; @@ -119,8 +124,7 @@ public: } virtual bool Match(const std::string &S) { - return S.find(Text) != std::string::npos || - Text.find(S) != std::string::npos; + return S.find(Text) != std::string::npos; } }; @@ -277,10 +281,14 @@ static void ParseDirective(const char *CommentStart, unsigned CommentLen, // skip optional whitespace PH.SkipWhitespace(); - // next optional token: positive integer + // next optional token: positive integer or a '+'. unsigned Count = 1; if (PH.Next(Count)) PH.Advance(); + else if (PH.Next("+")) { + Count = Directive::OneOrMoreCount; + PH.Advance(); + } // skip optional whitespace PH.SkipWhitespace(); @@ -340,7 +348,7 @@ static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED, FileID FID) { SourceManager& SM = PP.getSourceManager(); // Create a lexer to lex all the tokens of the main file in raw mode. const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); - Lexer RawLex(FID, FromFile, SM, PP.getLangOptions()); + Lexer RawLex(FID, FromFile, SM, PP.getLangOpts()); // Return comments as tokens, this is how we find expected diagnostics. RawLex.SetCommentRetentionState(true); @@ -370,7 +378,7 @@ static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr, const char *Kind, bool Expected) { if (diag_begin == diag_end) return 0; - llvm::SmallString<256> Fmt; + SmallString<256> Fmt; llvm::raw_svector_ostream OS(Fmt); for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { if (I->first.isInvalid() || !SourceMgr) @@ -391,7 +399,7 @@ static unsigned PrintProblem(DiagnosticsEngine &Diags, SourceManager *SourceMgr, if (DL.empty()) return 0; - llvm::SmallString<256> Fmt; + SmallString<256> Fmt; llvm::raw_svector_ostream OS(Fmt); for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) { Directive& D = **I; @@ -421,6 +429,7 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) { Directive& D = **I; unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.Location); + bool FoundOnce = false; for (unsigned i = 0; i < D.Count; ++i) { DiagList::iterator II, IE; @@ -434,19 +443,26 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr, break; } if (II == IE) { + if (D.Count == D.OneOrMoreCount) { + if (!FoundOnce) + LeftOnly.push_back(*I); + // We are only interested in at least one match, so exit the loop. + break; + } // Not found. LeftOnly.push_back(*I); } else { // Found. The same cannot be found twice. Right.erase(II); + FoundOnce = true; } } } // Now all that's left in Right are those that were not matched. - - return (PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true) + - PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(), - Label, false)); + unsigned num = PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true); + num += PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(), + Label, false); + return num; } /// CheckResults - This compares the expected results to those that diff --git a/contrib/llvm/tools/clang/lib/Frontend/Warnings.cpp b/contrib/llvm/tools/clang/lib/Frontend/Warnings.cpp index 8fbcd4b..ec5fde0 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/Warnings.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/Warnings.cpp @@ -31,6 +31,22 @@ #include <algorithm> using namespace clang; +// EmitUnknownDiagWarning - Emit a warning and typo hint for unknown warning +// opts +static void EmitUnknownDiagWarning(DiagnosticsEngine &Diags, + StringRef Prefix, StringRef Opt, + bool isPositive) { + StringRef Suggestion = DiagnosticIDs::getNearestWarningOption(Opt); + if (!Suggestion.empty()) + Diags.Report(isPositive? diag::warn_unknown_warning_option_suggest : + diag::warn_unknown_negative_warning_option_suggest) + << (Prefix.str() += Opt) << (Prefix.str() += Suggestion); + else + Diags.Report(isPositive? diag::warn_unknown_warning_option : + diag::warn_unknown_negative_warning_option) + << (Prefix.str() += Opt); +} + void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, const DiagnosticOptions &Opts) { Diags.setSuppressSystemWarnings(true); // Default to -Wno-system-headers @@ -43,6 +59,8 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, Diags.setErrorLimit(Opts.ErrorLimit); if (Opts.TemplateBacktraceLimit) Diags.setTemplateBacktraceLimit(Opts.TemplateBacktraceLimit); + if (Opts.ConstexprBacktraceLimit) + Diags.setConstexprBacktraceLimit(Opts.ConstexprBacktraceLimit); // If -pedantic or -pedantic-errors was specified, then we want to map all // extension diagnostics onto WARNING or ERROR unless the user has futz'd @@ -54,92 +72,120 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, else Diags.setExtensionHandlingBehavior(DiagnosticsEngine::Ext_Ignore); - for (unsigned i = 0, e = Opts.Warnings.size(); i != e; ++i) { - StringRef Opt = Opts.Warnings[i]; + llvm::SmallVector<diag::kind, 10> _Diags; + const IntrusiveRefCntPtr< DiagnosticIDs > DiagIDs = + Diags.getDiagnosticIDs(); + // We parse the warning options twice. The first pass sets diagnostic state, + // while the second pass reports warnings/errors. This has the effect that + // we follow the more canonical "last option wins" paradigm when there are + // conflicting options. + for (unsigned Report = 0, ReportEnd = 2; Report != ReportEnd; ++Report) { + bool SetDiagnostic = (Report == 0); + for (unsigned i = 0, e = Opts.Warnings.size(); i != e; ++i) { + StringRef Opt = Opts.Warnings[i]; - // Check to see if this warning starts with "no-", if so, this is a negative - // form of the option. - bool isPositive = true; - if (Opt.startswith("no-")) { - isPositive = false; - Opt = Opt.substr(3); - } + // Treat -Wformat=0 as an alias for -Wno-format. + if (Opt == "format=0") + Opt = "no-format"; - // Figure out how this option affects the warning. If -Wfoo, map the - // diagnostic to a warning, if -Wno-foo, map it to ignore. - diag::Mapping Mapping = isPositive ? diag::MAP_WARNING : diag::MAP_IGNORE; - - // -Wsystem-headers is a special case, not driven by the option table. It - // cannot be controlled with -Werror. - if (Opt == "system-headers") { - Diags.setSuppressSystemWarnings(!isPositive); - continue; - } - - // -Weverything is a special case as well. It implicitly enables all - // warnings, including ones not explicitly in a warning group. - if (Opt == "everything") { - Diags.setEnableAllWarnings(true); - continue; - } - - // -Werror/-Wno-error is a special case, not controlled by the option table. - // It also has the "specifier" form of -Werror=foo and -Werror-foo. - if (Opt.startswith("error")) { - StringRef Specifier; - if (Opt.size() > 5) { // Specifier must be present. - if ((Opt[5] != '=' && Opt[5] != '-') || Opt.size() == 6) { - Diags.Report(diag::warn_unknown_warning_specifier) - << "-Werror" << ("-W" + Opt.str()); - continue; - } - Specifier = Opt.substr(6); + // Check to see if this warning starts with "no-", if so, this is a + // negative form of the option. + bool isPositive = true; + if (Opt.startswith("no-")) { + isPositive = false; + Opt = Opt.substr(3); } - if (Specifier.empty()) { - Diags.setWarningsAsErrors(isPositive); + // Figure out how this option affects the warning. If -Wfoo, map the + // diagnostic to a warning, if -Wno-foo, map it to ignore. + diag::Mapping Mapping = isPositive ? diag::MAP_WARNING : diag::MAP_IGNORE; + + // -Wsystem-headers is a special case, not driven by the option table. It + // cannot be controlled with -Werror. + if (Opt == "system-headers") { + if (SetDiagnostic) + Diags.setSuppressSystemWarnings(!isPositive); continue; } - - // Set the warning as error flag for this specifier. - if (Diags.setDiagnosticGroupWarningAsError(Specifier, isPositive)) { - Diags.Report(isPositive ? diag::warn_unknown_warning_option : - diag::warn_unknown_negative_warning_option) - << ("-W" + Opt.str()); + + // -Weverything is a special case as well. It implicitly enables all + // warnings, including ones not explicitly in a warning group. + if (Opt == "everything") { + if (SetDiagnostic) { + if (isPositive) { + Diags.setEnableAllWarnings(true); + } else { + Diags.setEnableAllWarnings(false); + Diags.setMappingToAllDiagnostics(diag::MAP_IGNORE); + } + } + continue; } - continue; - } - - // -Wfatal-errors is yet another special case. - if (Opt.startswith("fatal-errors")) { - StringRef Specifier; - if (Opt.size() != 12) { - if ((Opt[12] != '=' && Opt[12] != '-') || Opt.size() == 13) { - Diags.Report(diag::warn_unknown_warning_specifier) - << "-Wfatal-errors" << ("-W" + Opt.str()); + + // -Werror/-Wno-error is a special case, not controlled by the option + // table. It also has the "specifier" form of -Werror=foo and -Werror-foo. + if (Opt.startswith("error")) { + StringRef Specifier; + if (Opt.size() > 5) { // Specifier must be present. + if ((Opt[5] != '=' && Opt[5] != '-') || Opt.size() == 6) { + if (Report) + Diags.Report(diag::warn_unknown_warning_specifier) + << "-Werror" << ("-W" + Opt.str()); + continue; + } + Specifier = Opt.substr(6); + } + + if (Specifier.empty()) { + if (SetDiagnostic) + Diags.setWarningsAsErrors(isPositive); continue; } - Specifier = Opt.substr(13); + + if (SetDiagnostic) { + // Set the warning as error flag for this specifier. + Diags.setDiagnosticGroupWarningAsError(Specifier, isPositive); + } else if (DiagIDs->getDiagnosticsInGroup(Specifier, _Diags)) { + EmitUnknownDiagWarning(Diags, "-Werror=", Specifier, isPositive); + } + continue; } + + // -Wfatal-errors is yet another special case. + if (Opt.startswith("fatal-errors")) { + StringRef Specifier; + if (Opt.size() != 12) { + if ((Opt[12] != '=' && Opt[12] != '-') || Opt.size() == 13) { + if (Report) + Diags.Report(diag::warn_unknown_warning_specifier) + << "-Wfatal-errors" << ("-W" + Opt.str()); + continue; + } + Specifier = Opt.substr(13); + } - if (Specifier.empty()) { - Diags.setErrorsAsFatal(isPositive); + if (Specifier.empty()) { + if (SetDiagnostic) + Diags.setErrorsAsFatal(isPositive); + continue; + } + + if (SetDiagnostic) { + // Set the error as fatal flag for this specifier. + Diags.setDiagnosticGroupErrorAsFatal(Specifier, isPositive); + } else if (DiagIDs->getDiagnosticsInGroup(Specifier, _Diags)) { + EmitUnknownDiagWarning(Diags, "-Wfatal-errors=", Specifier, + isPositive); + } continue; } - - // Set the error as fatal flag for this specifier. - if (Diags.setDiagnosticGroupErrorAsFatal(Specifier, isPositive)) { - Diags.Report(isPositive ? diag::warn_unknown_warning_option : - diag::warn_unknown_negative_warning_option) - << ("-W" + Opt.str()); + + if (Report) { + if (DiagIDs->getDiagnosticsInGroup(Opt, _Diags)) + EmitUnknownDiagWarning(Diags, "-W", Opt, isPositive); + } else { + Diags.setDiagnosticGroupMapping(Opt, Mapping); } - continue; - } - - if (Diags.setDiagnosticGroupMapping(Opt, Mapping)) { - Diags.Report(isPositive ? diag::warn_unknown_warning_option : - diag::warn_unknown_negative_warning_option) - << ("-W" + Opt.str()); } } } |