diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h index 8fc074758630..653142dbee38 100644 --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -314,7 +314,10 @@ public: virtual ~ExternalSLocEntrySource(); /// \brief Read the source location entry with index ID. - virtual void ReadSLocEntry(unsigned ID) = 0; + /// + /// \returns true if an error occurred that prevented the source-location + /// entry from being loaded. + virtual bool ReadSLocEntry(unsigned ID) = 0; }; @@ -377,7 +380,7 @@ public: /// Spelling locations represent where the bytes corresponding to a token came /// from and instantiation locations represent where the location is in the /// user's view. In the case of a macro expansion, for example, the spelling -/// location indicates where the expanded token came from and the instantiation +/// location indicates where the expanded token came from and the instantiation /// location specifies where it was expanded. class SourceManager : public llvm::RefCountedBase { /// \brief Diagnostic object. @@ -445,6 +448,9 @@ class SourceManager : public llvm::RefCountedBase { // Cache results for the isBeforeInTranslationUnit method. mutable IsBeforeInTranslationUnitCache IsBeforeInTUCache; + // Cache for the "fake" buffer used for error-recovery purposes. + mutable llvm::MemoryBuffer *FakeBufferForRecovery; + // SourceManager doesn't support copy construction. explicit SourceManager(const SourceManager&); void operator=(const SourceManager&); @@ -571,18 +577,42 @@ public: /// buffer and returns a non-empty error string. const llvm::MemoryBuffer *getBuffer(FileID FID, SourceLocation Loc, bool *Invalid = 0) const { - return getSLocEntry(FID).getFile().getContentCache() - ->getBuffer(Diag, *this, Loc, Invalid); + bool MyInvalid = false; + const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &MyInvalid); + if (MyInvalid || !Entry.isFile()) { + if (Invalid) + *Invalid = true; + + return getFakeBufferForRecovery(); + } + + return Entry.getFile().getContentCache()->getBuffer(Diag, *this, Loc, + Invalid); } const llvm::MemoryBuffer *getBuffer(FileID FID, bool *Invalid = 0) const { - return getSLocEntry(FID).getFile().getContentCache() - ->getBuffer(Diag, *this, SourceLocation(), Invalid); + bool MyInvalid = false; + const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &MyInvalid); + if (MyInvalid || !Entry.isFile()) { + if (Invalid) + *Invalid = true; + + return getFakeBufferForRecovery(); + } + + return Entry.getFile().getContentCache()->getBuffer(Diag, *this, + SourceLocation(), + Invalid); } /// getFileEntryForID - Returns the FileEntry record for the provided FileID. const FileEntry *getFileEntryForID(FileID FID) const { - return getSLocEntry(FID).getFile().getContentCache()->OrigEntry; + bool MyInvalid = false; + const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &MyInvalid); + if (MyInvalid || !Entry.isFile()) + return 0; + + return Entry.getFile().getContentCache()->OrigEntry; } /// Returns the FileEntry record for the provided SLocEntry. @@ -622,8 +652,12 @@ public: /// first byte of the specified file. SourceLocation getLocForStartOfFile(FileID FID) const { assert(FID.ID < SLocEntryTable.size() && "FileID out of range"); - assert(getSLocEntry(FID).isFile() && "FileID is not a file"); - unsigned FileOffset = getSLocEntry(FID).getOffset(); + bool Invalid = false; + const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &Invalid); + if (Invalid || !Entry.isFile()) + return SourceLocation(); + + unsigned FileOffset = Entry.getOffset(); return SourceLocation::getFileLoc(FileOffset); } @@ -851,17 +885,22 @@ public: // any other external source). unsigned sloc_loaded_entry_size() const { return SLocEntryLoaded.size(); } - const SrcMgr::SLocEntry &getSLocEntry(unsigned ID) const { + const SrcMgr::SLocEntry &getSLocEntry(unsigned ID, bool *Invalid = 0) const { assert(ID < SLocEntryTable.size() && "Invalid id"); + // If we haven't loaded this source-location entry from the external source + // yet, do so now. if (ExternalSLocEntries && ID < SLocEntryLoaded.size() && - !SLocEntryLoaded[ID]) - ExternalSLocEntries->ReadSLocEntry(ID); + !SLocEntryLoaded[ID] && + ExternalSLocEntries->ReadSLocEntry(ID) && + Invalid) + *Invalid = true; + return SLocEntryTable[ID]; } - const SrcMgr::SLocEntry &getSLocEntry(FileID FID) const { - return getSLocEntry(FID.ID); + const SrcMgr::SLocEntry &getSLocEntry(FileID FID, bool *Invalid = 0) const { + return getSLocEntry(FID.ID, Invalid); } unsigned getNextOffset() const { return NextOffset; } @@ -877,6 +916,8 @@ public: void ClearPreallocatedSLocEntries(); private: + const llvm::MemoryBuffer *getFakeBufferForRecovery() const; + /// isOffsetInFileID - Return true if the specified FileID contains the /// specified SourceLocation offset. This is a very hot method. inline bool isOffsetInFileID(FileID FID, unsigned SLocOffset) const { diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 559d14554b61..ce2c07553873 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1120,7 +1120,7 @@ public: } /// \brief Read the source location entry with index ID. - virtual void ReadSLocEntry(unsigned ID); + virtual bool ReadSLocEntry(unsigned ID); Selector DecodeSelector(unsigned Idx); diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp index d73a0af419e0..72c0c08e6aa8 100644 --- a/clang/lib/Basic/SourceManager.cpp +++ b/clang/lib/Basic/SourceManager.cpp @@ -278,7 +278,12 @@ void SourceManager::AddLineNote(SourceLocation Loc, unsigned LineNo, int FilenameID) { std::pair LocInfo = getDecomposedInstantiationLoc(Loc); - const SrcMgr::FileInfo &FileInfo = getSLocEntry(LocInfo.first).getFile(); + bool Invalid = false; + const SLocEntry &Entry = getSLocEntry(LocInfo.first, &Invalid); + if (!Entry.isFile() || Invalid) + return; + + const SrcMgr::FileInfo &FileInfo = Entry.getFile(); // Remember that this file has #line directives now if it doesn't already. const_cast(FileInfo).setHasLineDirectives(); @@ -302,7 +307,13 @@ void SourceManager::AddLineNote(SourceLocation Loc, unsigned LineNo, } std::pair LocInfo = getDecomposedInstantiationLoc(Loc); - const SrcMgr::FileInfo &FileInfo = getSLocEntry(LocInfo.first).getFile(); + + bool Invalid = false; + const SLocEntry &Entry = getSLocEntry(LocInfo.first, &Invalid); + if (!Entry.isFile() || Invalid) + return; + + const SrcMgr::FileInfo &FileInfo = Entry.getFile(); // Remember that this file has #line directives now if it doesn't already. const_cast(FileInfo).setHasLineDirectives(); @@ -341,7 +352,7 @@ LineTableInfo &SourceManager::getLineTable() { SourceManager::SourceManager(Diagnostic &Diag, FileManager &FileMgr) : Diag(Diag), FileMgr(FileMgr), OverridenFilesKeepOriginalName(true), ExternalSLocEntries(0), LineTable(0), NumLinearScans(0), - NumBinaryProbes(0) { + NumBinaryProbes(0), FakeBufferForRecovery(0) { clearIDTables(); Diag.setSourceManager(this); } @@ -361,6 +372,8 @@ SourceManager::~SourceManager() { I->second->~ContentCache(); ContentCacheAlloc.Deallocate(I->second); } + + delete FakeBufferForRecovery; } void SourceManager::clearIDTables() { @@ -455,6 +468,15 @@ void SourceManager::ClearPreallocatedSLocEntries() { ExternalSLocEntries = 0; } +/// \brief As part of recovering from missing or changed content, produce a +/// fake, non-empty buffer. +const llvm::MemoryBuffer *SourceManager::getFakeBufferForRecovery() const { + if (!FakeBufferForRecovery) + FakeBufferForRecovery + = llvm::MemoryBuffer::getMemBuffer("<<>"); + + return FakeBufferForRecovery; +} //===----------------------------------------------------------------------===// // Methods to create new FileID's and instantiations. @@ -554,8 +576,8 @@ void SourceManager::overrideFileContents(const FileEntry *SourceFile, llvm::StringRef SourceManager::getBufferData(FileID FID, bool *Invalid) const { bool MyInvalid = false; - const SLocEntry &SLoc = getSLocEntry(FID.ID); - if (!SLoc.isFile()) { + const SLocEntry &SLoc = getSLocEntry(FID.ID, &MyInvalid); + if (!SLoc.isFile() || MyInvalid) { if (Invalid) *Invalid = true; return "<<<<>>>>"; @@ -583,7 +605,8 @@ llvm::StringRef SourceManager::getBufferData(FileID FID, bool *Invalid) const { /// SLocEntryTable which contains the specified location. /// FileID SourceManager::getFileIDSlow(unsigned SLocOffset) const { - assert(SLocOffset && "Invalid FileID"); + if (!SLocOffset) + return FileID::get(0); // After the first and second level caches, I see two common sorts of // behavior: 1) a lot of searched FileID's are "near" the cached file location @@ -611,8 +634,13 @@ FileID SourceManager::getFileIDSlow(unsigned SLocOffset) const { unsigned NumProbes = 0; while (1) { --I; - if (ExternalSLocEntries) - getSLocEntry(FileID::get(I - SLocEntryTable.begin())); + if (ExternalSLocEntries) { + bool Invalid = false; + getSLocEntry(FileID::get(I - SLocEntryTable.begin()), &Invalid); + if (Invalid) + return FileID::get(0); + } + if (I->getOffset() <= SLocOffset) { #if 0 printf("lin %d -> %d [%s] %d %d\n", SLocOffset, @@ -642,9 +670,13 @@ FileID SourceManager::getFileIDSlow(unsigned SLocOffset) const { unsigned LessIndex = 0; NumProbes = 0; while (1) { + bool Invalid = false; unsigned MiddleIndex = (GreaterIndex-LessIndex)/2+LessIndex; - unsigned MidOffset = getSLocEntry(FileID::get(MiddleIndex)).getOffset(); - + unsigned MidOffset = getSLocEntry(FileID::get(MiddleIndex), &Invalid) + .getOffset(); + if (Invalid) + return FileID::get(0); + ++NumProbes; // If the offset of the midpoint is too large, chop the high side of the @@ -794,9 +826,16 @@ const char *SourceManager::getCharacterData(SourceLocation SL, // Note that calling 'getBuffer()' may lazily page in a source file. bool CharDataInvalid = false; + const SLocEntry &Entry = getSLocEntry(LocInfo.first, &CharDataInvalid); + if (CharDataInvalid || !Entry.isFile()) { + if (Invalid) + *Invalid = true; + + return "<<<>>>"; + } const llvm::MemoryBuffer *Buffer - = getSLocEntry(LocInfo.first).getFile().getContentCache() - ->getBuffer(Diag, *this, SourceLocation(), &CharDataInvalid); + = Entry.getFile().getContentCache() + ->getBuffer(Diag, *this, SourceLocation(), &CharDataInvalid); if (Invalid) *Invalid = CharDataInvalid; return Buffer->getBufferStart() + (CharDataInvalid? 0 : LocInfo.second); @@ -912,10 +951,18 @@ unsigned SourceManager::getLineNumber(FileID FID, unsigned FilePos, ContentCache *Content; if (LastLineNoFileIDQuery == FID) Content = LastLineNoContentCache; - else - Content = const_cast(getSLocEntry(FID) - .getFile().getContentCache()); - + else { + bool MyInvalid = false; + const SLocEntry &Entry = getSLocEntry(FID, &MyInvalid); + if (MyInvalid || !Entry.isFile()) { + if (Invalid) + *Invalid = true; + return 1; + } + + Content = const_cast(Entry.getFile().getContentCache()); + } + // If this is the first use of line information for this buffer, compute the /// SourceLineCache for it on demand. if (Content->SourceLineCache == 0) { @@ -1042,7 +1089,12 @@ SrcMgr::CharacteristicKind SourceManager::getFileCharacteristic(SourceLocation Loc) const { assert(!Loc.isInvalid() && "Can't get file characteristic of invalid loc!"); std::pair LocInfo = getDecomposedInstantiationLoc(Loc); - const SrcMgr::FileInfo &FI = getSLocEntry(LocInfo.first).getFile(); + bool Invalid = false; + const SLocEntry &SEntry = getSLocEntry(LocInfo.first, &Invalid); + if (Invalid || !SEntry.isFile()) + return C_User; + + const SrcMgr::FileInfo &FI = SEntry.getFile(); // If there are no #line directives in this file, just return the whole-file // state. @@ -1085,7 +1137,12 @@ PresumedLoc SourceManager::getPresumedLoc(SourceLocation Loc) const { // Presumed locations are always for instantiation points. std::pair LocInfo = getDecomposedInstantiationLoc(Loc); - const SrcMgr::FileInfo &FI = getSLocEntry(LocInfo.first).getFile(); + bool Invalid = false; + const SLocEntry &Entry = getSLocEntry(LocInfo.first, &Invalid); + if (Invalid || !Entry.isFile()) + return PresumedLoc(); + + const SrcMgr::FileInfo &FI = Entry.getFile(); const SrcMgr::ContentCache *C = FI.getContentCache(); // To get the source name, first consult the FileEntry (if one exists) @@ -1096,7 +1153,7 @@ PresumedLoc SourceManager::getPresumedLoc(SourceLocation Loc) const { Filename = C->OrigEntry->getName(); else Filename = C->getBuffer(Diag, *this)->getBufferIdentifier(); - bool Invalid = false; + unsigned LineNo = getLineNumber(LocInfo.first, LocInfo.second, &Invalid); if (Invalid) return PresumedLoc(); @@ -1173,7 +1230,11 @@ SourceLocation SourceManager::getLocation(const FileEntry *SourceFile, llvm::Optional SourceFileInode; llvm::Optional SourceFileName; if (!MainFileID.isInvalid()) { - const SLocEntry &MainSLoc = getSLocEntry(MainFileID); + bool Invalid = false; + const SLocEntry &MainSLoc = getSLocEntry(MainFileID, &Invalid); + if (Invalid) + return SourceLocation(); + if (MainSLoc.isFile()) { const ContentCache *MainContentCache = MainSLoc.getFile().getContentCache(); @@ -1206,7 +1267,11 @@ SourceLocation SourceManager::getLocation(const FileEntry *SourceFile, // The location we're looking for isn't in the main file; look // through all of the source locations. for (unsigned I = 0, N = sloc_entry_size(); I != N; ++I) { - const SLocEntry &SLoc = getSLocEntry(I); + bool Invalid = false; + const SLocEntry &SLoc = getSLocEntry(I, &Invalid); + if (Invalid) + return SourceLocation(); + if (SLoc.isFile() && SLoc.getFile().getContentCache() && SLoc.getFile().getContentCache()->OrigEntry == SourceFile) { @@ -1224,8 +1289,12 @@ SourceLocation SourceManager::getLocation(const FileEntry *SourceFile, (SourceFileName = llvm::sys::path::filename(SourceFile->getName()))) && (SourceFileInode || (SourceFileInode = getActualFileInode(SourceFile)))) { + bool Invalid = false; for (unsigned I = 0, N = sloc_entry_size(); I != N; ++I) { - const SLocEntry &SLoc = getSLocEntry(I); + const SLocEntry &SLoc = getSLocEntry(I, &Invalid); + if (Invalid) + return SourceLocation(); + if (SLoc.isFile()) { const ContentCache *FileContentCache = SLoc.getFile().getContentCache(); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index fe098c95a7c0..dafce43a5d1e 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -4383,8 +4383,8 @@ IdentifierInfo *ASTReader::DecodeIdentifierInfo(unsigned ID) { return IdentifiersLoaded[ID]; } -void ASTReader::ReadSLocEntry(unsigned ID) { - ReadSLocEntryRecord(ID); +bool ASTReader::ReadSLocEntry(unsigned ID) { + return ReadSLocEntryRecord(ID) != Success; } Selector ASTReader::DecodeSelector(unsigned ID) { diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index dc74cd35ea73..90d9881ce104 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -2773,8 +2773,9 @@ void clang_getInstantiationLocation(CXSourceLocation location, // Check that the FileID is invalid on the instantiation location. // This can manifest in invalid code. FileID fileID = SM.getFileID(InstLoc); - const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(fileID); - if (!sloc.isFile()) { + bool Invalid = false; + const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(fileID, &Invalid); + if (!sloc.isFile() || Invalid) { createNullLocation(file, line, column, offset); return; } diff --git a/clang/tools/libclang/CIndexInclusionStack.cpp b/clang/tools/libclang/CIndexInclusionStack.cpp index 9ea901927f8b..6bc4f2e776a3 100644 --- a/clang/tools/libclang/CIndexInclusionStack.cpp +++ b/clang/tools/libclang/CIndexInclusionStack.cpp @@ -40,10 +40,10 @@ void clang_getInclusions(CXTranslationUnit TU, CXInclusionVisitor CB, i = 0; for ( ; i < n ; ++i) { - - const SrcMgr::SLocEntry &SL = SM.getSLocEntry(i); + bool Invalid = false; + const SrcMgr::SLocEntry &SL = SM.getSLocEntry(i, &Invalid); - if (!SL.isFile()) + if (!SL.isFile() || Invalid) continue; const SrcMgr::FileInfo &FI = SL.getFile();