diff --git a/clang/include/clang/Basic/FileEntry.h b/clang/include/clang/Basic/FileEntry.h --- a/clang/include/clang/Basic/FileEntry.h +++ b/clang/include/clang/Basic/FileEntry.h @@ -341,6 +341,7 @@ llvm::sys::fs::UniqueID UniqueID; unsigned UID = 0; // A unique (small) ID for the file. bool IsNamedPipe = false; + bool IsValid = false; /// The open file, if it is owned by the \p FileEntry. mutable std::unique_ptr File; @@ -362,6 +363,7 @@ FileEntryRef getLastRef() const { return *LastRef; } StringRef tryGetRealPathName() const { return RealPathName; } + bool isValid() const { return IsValid; } off_t getSize() const { return Size; } unsigned getUID() const { return UID; } const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; } diff --git a/clang/include/clang/Basic/FileManager.h b/clang/include/clang/Basic/FileManager.h --- a/clang/include/clang/Basic/FileManager.h +++ b/clang/include/clang/Basic/FileManager.h @@ -18,6 +18,7 @@ #include "clang/Basic/FileEntry.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceManager.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/PointerUnion.h" @@ -31,6 +32,7 @@ #include #include #include +#include #include namespace llvm { @@ -94,6 +96,11 @@ llvm::StringMap, llvm::BumpPtrAllocator> SeenFileEntries; + std::set FileEntriesToReread; + + friend void FileManagerTestHelper(FileManager &); + friend void SourceManagerTestHelper(SourceManager &, FileManager &); + /// A mirror of SeenFileEntries to give fake answers for getBypassFile(). /// /// Don't bother hooking up a BumpPtrAllocator. This should be rarely used, @@ -295,6 +302,9 @@ std::error_code getNoncachedStatValue(StringRef Path, llvm::vfs::Status &Result); + /// Remove the real file \p Entry from the cache. + void invalidateCache(FileEntry *Entry); + /// If path is not absolute and FileSystemOptions set the working /// directory, the path is modified to be relative to the given /// working directory. diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -260,6 +260,7 @@ // If BufStr has an invalid BOM, returns the BOM name; otherwise, returns // nullptr static const char *getInvalidBOM(StringRef BufStr); + friend void SourceManagerTestHelper(SourceManager &, FileManager &); }; // Assert that the \c ContentCache objects will always be 8-byte aligned so @@ -797,6 +798,8 @@ ~SourceManager(); void clearIDTables(); + friend void SourceManagerTestHelper(SourceManager &, FileManager &); + void invalidateCache(FileID FID); /// Initialize this source manager suitably to replay the compilation /// described by \p Old. Requires that \p Old outlive \p *this. diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp --- a/clang/lib/Basic/FileManager.cpp +++ b/clang/lib/Basic/FileManager.cpp @@ -635,6 +635,12 @@ return std::error_code(); } +void FileManager::invalidateCache(FileEntry *Entry) { + assert(Entry && "Cannot invalidate a NULL FileEntry"); + FileEntriesToReread.insert(Entry->getLastRef()); + Entry->IsValid = false; +} + void FileManager::GetUniqueIDMapping( SmallVectorImpl &UIDToFiles) const { UIDToFiles.clear(); diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp --- a/clang/lib/Basic/SourceManager.cpp +++ b/clang/lib/Basic/SourceManager.cpp @@ -358,6 +358,25 @@ return false; } +void SourceManager::invalidateCache(FileID FID) { + const FileEntry *Entry = getFileEntryForID(FID); + if (!Entry) + return; + if (ContentCache *&E = FileInfos[Entry]) { + E->setBuffer(nullptr); + E = 0; + } + if (!FID.isInvalid()) { + const SrcMgr::SLocEntry &SLocE = getSLocEntry(FID); + if (SLocE.isFile()) { + SrcMgr::ContentCache &CC = + const_cast(SLocE.getFile().getContentCache()); + CC.setBuffer(nullptr); + } + } + getFileManager().invalidateCache(const_cast(Entry)); +} + void SourceManager::initializeForReplay(const SourceManager &Old) { assert(MainFileID.isInvalid() && "expected uninitialized SourceManager"); diff --git a/clang/unittests/Basic/FileManagerTest.cpp b/clang/unittests/Basic/FileManagerTest.cpp --- a/clang/unittests/Basic/FileManagerTest.cpp +++ b/clang/unittests/Basic/FileManagerTest.cpp @@ -18,6 +18,10 @@ using namespace llvm; using namespace clang; +void clang::FileManagerTestHelper(FileManager &manager) { + ASSERT_TRUE(!manager.FileEntriesToReread.empty()); +} + namespace { // Used to create a fake file system for running the tests with such @@ -99,6 +103,29 @@ FileManager manager; }; +// If file entry valid, mark the file entry invalid, until reread. +TEST_F(FileManagerTest, invalidateCacheSuccess) { + auto statCache = std::make_unique(); + statCache->InjectFile("file.cpp", 42); + + manager.setStatCache(std::move(statCache)); + manager.getVirtualFile("file.cpp", 100, 0); + auto file = manager.getFile("file.cpp"); + + // Check for file null assertion success + // manager.invalidateCache(NULL); + + auto FileRef = manager.getFileRef("file.cpp"); + + ASSERT_FALSE(!FileRef); + + FileEntry *FileEntryObj = const_cast(&FileRef->getFileEntry()); + manager.invalidateCache(FileEntryObj); + FileManagerTestHelper(manager); + + ASSERT_FALSE(FileEntryObj->isValid()); +} + // When a virtual file is added, its getDir() field has correct name. TEST_F(FileManagerTest, getVirtualFileSetsTheDirFieldCorrectly) { FileEntryRef file = manager.getVirtualFileRef("foo.cpp", 42, 0); diff --git a/clang/unittests/Basic/SourceManagerTest.cpp b/clang/unittests/Basic/SourceManagerTest.cpp --- a/clang/unittests/Basic/SourceManagerTest.cpp +++ b/clang/unittests/Basic/SourceManagerTest.cpp @@ -26,6 +26,15 @@ using namespace clang; +void clang::SourceManagerTestHelper(SourceManager &SourceMgr, + FileManager &FileMgr) { + FileID mainFileID = SourceMgr.getMainFileID(); + const SrcMgr::ContentCache *Cache = + &(SourceMgr.getSLocEntry(mainFileID).getFile().getContentCache()); + // EXPECT_FALSE(Cache->Buffer); + ASSERT_TRUE(FileMgr.FileEntriesToReread.empty()); +} + namespace { // The test fixture. @@ -51,6 +60,36 @@ IntrusiveRefCntPtr Target; }; +// Test for invalidate cache success, making the file entry invalid, until +// reread +TEST_F(SourceManagerTest, invalidateCacheSuccess) { + const char *Source = "int x;"; + + std::unique_ptr Buf = + llvm::MemoryBuffer::getMemBuffer(Source); + const FileEntry *SourceFile = + FileMgr.getVirtualFile("mainFile.cpp", Buf->getBufferSize(), 0); + + FileID mainFileID = SourceMgr.createFileID(std::move(Buf)); + SourceMgr.overrideFileContents(SourceFile, std::move(Buf)); + SourceMgr.setMainFileID(mainFileID); + + SourceMgr.invalidateCache(mainFileID); + + EXPECT_FALSE(SourceFile->isValid()); + EXPECT_FALSE(mainFileID.isInvalid()); + + const SrcMgr::ContentCache *Cache = + &(SourceMgr.getSLocEntry(mainFileID).getFile().getContentCache()); + + EXPECT_EQ(SourceMgr.getNonBuiltinFilenameForID(mainFileID), None); + // EXPECT_EQ(SourceMgr.getBufferDataIfLoaded(mainFileID), None); + + EXPECT_FALSE(Cache->IsBufferInvalid); + + SourceManagerTestHelper(SourceMgr, FileMgr); +} + TEST_F(SourceManagerTest, isBeforeInTranslationUnit) { const char *source = "#define M(x) [x]\n"