Index: include/clang/Frontend/ASTUnit.h =================================================================== --- include/clang/Frontend/ASTUnit.h +++ include/clang/Frontend/ASTUnit.h @@ -192,6 +192,9 @@ /// some number of calls. unsigned PreambleRebuildCounter; + /// \brief Counter indicating how often the preamble was build in total. + unsigned PreambleCounter; + /// \brief Cache pairs "filename - source location" /// /// Cache contains only source locations from preamble so it is @@ -547,7 +550,11 @@ return SourceRange(mapLocationToPreamble(R.getBegin()), mapLocationToPreamble(R.getEnd())); } - + + unsigned getPreambleCounter() const { + return PreambleCounter; + } + // Retrieve the diagnostics associated with this AST typedef StoredDiagnostic *stored_diag_iterator; typedef const StoredDiagnostic *stored_diag_const_iterator; Index: lib/Frontend/ASTUnit.cpp =================================================================== --- lib/Frontend/ASTUnit.cpp +++ lib/Frontend/ASTUnit.cpp @@ -190,6 +190,7 @@ OwnsRemappedFileBuffers(true), NumStoredDiagnosticsFromDriver(0), PreambleRebuildCounter(0), + PreambleCounter(0), NumWarningsInPreamble(0), ShouldCacheCodeCompletionResults(false), IncludeBriefCommentsInCodeCompletion(false), UserFilesAreVolatile(false), @@ -1296,6 +1297,7 @@ PCHContainerOps, /*StoreInMemory=*/false, Callbacks); if (NewPreamble) { Preamble = std::move(*NewPreamble); + ++PreambleCounter; PreambleRebuildCounter = 1; } else { switch (static_cast(NewPreamble.getError().value())) { Index: lib/Frontend/PrecompiledPreamble.cpp =================================================================== --- lib/Frontend/PrecompiledPreamble.cpp +++ lib/Frontend/PrecompiledPreamble.cpp @@ -434,21 +434,28 @@ Status.getSize(), llvm::sys::toTimeT(Status.getLastModificationTime())); } + llvm::StringMap OverridenFileBuffers; for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) { - vfs::Status Status; - if (!moveOnNoError(VFS->status(RB.first), Status)) - return false; - - OverriddenFiles[Status.getUniqueID()] = + OverridenFileBuffers[RB.first] = PreambleFileHash::createForMemoryBuffer(RB.second); } // Check whether anything has changed. for (const auto &F : FilesInPreamble) { + auto OverridenFileBuffer = OverridenFileBuffers.find(F.first()); + if (OverridenFileBuffer != OverridenFileBuffers.end()) { + // The file's buffer was remapped; check whether it matches up + // with the previous mapping. + if (OverridenFileBuffer->second != F.second) + return false; + continue; + } + vfs::Status Status; if (!moveOnNoError(VFS->status(F.first()), Status)) { - // If we can't stat the file, assume that something horrible happened. - return false; + // If the file's buffer is not remapped and we can't stat it, + // assume that something horrible happened. + return false; } std::map::iterator Overridden = @@ -461,9 +468,10 @@ continue; } - // The file was not remapped; check whether it has changed on disk. + // Neither the file's buffer nor the file itself was remapped; + // check whether it has changed on disk. if (Status.getSize() != uint64_t(F.second.Size) || - llvm::sys::toTimeT(Status.getLastModificationTime()) != + llvm::sys::toTimeT(Status.getLastModificationTime()) != F.second.ModTime) return false; } Index: unittests/Frontend/PCHPreambleTest.cpp =================================================================== --- unittests/Frontend/PCHPreambleTest.cpp +++ unittests/Frontend/PCHPreambleTest.cpp @@ -124,6 +124,22 @@ } }; +TEST_F(PCHPreambleTest, ReparseDoesNotInvalidatePreambleDueToNotExistingUnsavedFile) { + std::string Header1 = "//./header1.h"; + std::string MainName = "//./main.cpp"; + AddFile(MainName, "#include \"//./header1.h\"\n" + "int main() { return ZERO; }"); + + std::unique_ptr AST(ParseAST(MainName)); + ASSERT_TRUE(AST.get()); + ASSERT_EQ(AST->getPreambleCounter(), 1U); + + RemapFile(Header1, "#define ZERO 0\n"); + ASSERT_TRUE(ReparseAST(AST)); + + ASSERT_EQ(AST->getPreambleCounter(), 1U); +} + TEST_F(PCHPreambleTest, ReparseWithOverriddenFileDoesNotInvalidatePreamble) { std::string Header1 = "//./header1.h"; std::string Header2 = "//./header2.h";