PCHPreambleTest.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. //====-- unittests/Frontend/PCHPreambleTest.cpp - FrontendAction tests ---====//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. #include "clang/Frontend/ASTUnit.h"
  10. #include "clang/Frontend/CompilerInvocation.h"
  11. #include "clang/Frontend/CompilerInstance.h"
  12. #include "clang/Frontend/FrontendActions.h"
  13. #include "clang/Frontend/FrontendOptions.h"
  14. #include "clang/Lex/PreprocessorOptions.h"
  15. #include "clang/Basic/Diagnostic.h"
  16. #include "clang/Basic/FileManager.h"
  17. #include "llvm/Support/FileSystem.h"
  18. #include "llvm/Support/MemoryBuffer.h"
  19. #include "llvm/Support/Path.h"
  20. #include "gtest/gtest.h"
  21. using namespace llvm;
  22. using namespace clang;
  23. namespace {
  24. class ReadCountingInMemoryFileSystem : public vfs::InMemoryFileSystem
  25. {
  26. std::map<std::string, unsigned> ReadCounts;
  27. public:
  28. ErrorOr<std::unique_ptr<vfs::File>> openFileForRead(const Twine &Path) override
  29. {
  30. SmallVector<char, 128> PathVec;
  31. Path.toVector(PathVec);
  32. llvm::sys::path::remove_dots(PathVec, true);
  33. ++ReadCounts[std::string(PathVec.begin(), PathVec.end())];
  34. return InMemoryFileSystem::openFileForRead(Path);
  35. }
  36. unsigned GetReadCount(const Twine &Path) const
  37. {
  38. auto it = ReadCounts.find(Path.str());
  39. return it == ReadCounts.end() ? 0 : it->second;
  40. }
  41. };
  42. class PCHPreambleTest : public ::testing::Test {
  43. IntrusiveRefCntPtr<ReadCountingInMemoryFileSystem> VFS;
  44. StringMap<std::string> RemappedFiles;
  45. std::shared_ptr<PCHContainerOperations> PCHContainerOpts;
  46. FileSystemOptions FSOpts;
  47. public:
  48. void SetUp() override {
  49. VFS = new ReadCountingInMemoryFileSystem();
  50. // We need the working directory to be set to something absolute,
  51. // otherwise it ends up being inadvertently set to the current
  52. // working directory in the real file system due to a series of
  53. // unfortunate conditions interacting badly.
  54. // What's more, this path *must* be absolute on all (real)
  55. // filesystems, so just '/' won't work (e.g. on Win32).
  56. VFS->setCurrentWorkingDirectory("//./");
  57. }
  58. void TearDown() override {
  59. }
  60. void AddFile(const std::string &Filename, const std::string &Contents) {
  61. ::time_t now;
  62. ::time(&now);
  63. VFS->addFile(Filename, now, MemoryBuffer::getMemBufferCopy(Contents, Filename));
  64. }
  65. void RemapFile(const std::string &Filename, const std::string &Contents) {
  66. RemappedFiles[Filename] = Contents;
  67. }
  68. std::unique_ptr<ASTUnit> ParseAST(const std::string &EntryFile) {
  69. PCHContainerOpts = std::make_shared<PCHContainerOperations>();
  70. std::shared_ptr<CompilerInvocation> CI(new CompilerInvocation);
  71. CI->getFrontendOpts().Inputs.push_back(
  72. FrontendInputFile(EntryFile, FrontendOptions::getInputKindForExtension(
  73. llvm::sys::path::extension(EntryFile).substr(1))));
  74. CI->getTargetOpts().Triple = "i386-unknown-linux-gnu";
  75. CI->getPreprocessorOpts().RemappedFileBuffers = GetRemappedFiles();
  76. PreprocessorOptions &PPOpts = CI->getPreprocessorOpts();
  77. PPOpts.RemappedFilesKeepOriginalName = true;
  78. IntrusiveRefCntPtr<DiagnosticsEngine>
  79. Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions, new DiagnosticConsumer));
  80. FileManager *FileMgr = new FileManager(FSOpts, VFS);
  81. std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
  82. CI, PCHContainerOpts, Diags, FileMgr, false, false,
  83. /*PrecompilePreambleAfterNParses=*/1);
  84. return AST;
  85. }
  86. bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) {
  87. bool reparseFailed = AST->Reparse(PCHContainerOpts, GetRemappedFiles(), VFS);
  88. return !reparseFailed;
  89. }
  90. unsigned GetFileReadCount(const std::string &Filename) const {
  91. return VFS->GetReadCount(Filename);
  92. }
  93. private:
  94. std::vector<std::pair<std::string, llvm::MemoryBuffer *>>
  95. GetRemappedFiles() const {
  96. std::vector<std::pair<std::string, llvm::MemoryBuffer *>> Remapped;
  97. for (const auto &RemappedFile : RemappedFiles) {
  98. std::unique_ptr<MemoryBuffer> buf = MemoryBuffer::getMemBufferCopy(
  99. RemappedFile.second, RemappedFile.first());
  100. Remapped.emplace_back(RemappedFile.first(), buf.release());
  101. }
  102. return Remapped;
  103. }
  104. };
  105. TEST_F(PCHPreambleTest, ReparseWithOverriddenFileDoesNotInvalidatePreamble) {
  106. std::string Header1 = "//./header1.h";
  107. std::string Header2 = "//./header2.h";
  108. std::string MainName = "//./main.cpp";
  109. AddFile(Header1, "");
  110. AddFile(Header2, "#pragma once");
  111. AddFile(MainName,
  112. "#include \"//./foo/../header1.h\"\n"
  113. "#include \"//./foo/../header2.h\"\n"
  114. "int main() { return ZERO; }");
  115. RemapFile(Header1, "static const int ZERO = 0;\n");
  116. std::unique_ptr<ASTUnit> AST(ParseAST(MainName));
  117. ASSERT_TRUE(AST.get());
  118. ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
  119. unsigned initialCounts[] = {
  120. GetFileReadCount(MainName),
  121. GetFileReadCount(Header1),
  122. GetFileReadCount(Header2)
  123. };
  124. ASSERT_TRUE(ReparseAST(AST));
  125. ASSERT_NE(initialCounts[0], GetFileReadCount(MainName));
  126. ASSERT_EQ(initialCounts[1], GetFileReadCount(Header1));
  127. ASSERT_EQ(initialCounts[2], GetFileReadCount(Header2));
  128. }
  129. TEST_F(PCHPreambleTest, ParseWithBom) {
  130. std::string Header = "//./header.h";
  131. std::string Main = "//./main.cpp";
  132. AddFile(Header, "int random() { return 4; }");
  133. AddFile(Main,
  134. "\xef\xbb\xbf"
  135. "#include \"//./header.h\"\n"
  136. "int main() { return random() -2; }");
  137. std::unique_ptr<ASTUnit> AST(ParseAST(Main));
  138. ASSERT_TRUE(AST.get());
  139. ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
  140. unsigned HeaderReadCount = GetFileReadCount(Header);
  141. ASSERT_TRUE(ReparseAST(AST));
  142. ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
  143. // Check preamble PCH was really reused
  144. ASSERT_EQ(HeaderReadCount, GetFileReadCount(Header));
  145. // Remove BOM
  146. RemapFile(Main,
  147. "#include \"//./header.h\"\n"
  148. "int main() { return random() -2; }");
  149. ASSERT_TRUE(ReparseAST(AST));
  150. ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
  151. ASSERT_LE(HeaderReadCount, GetFileReadCount(Header));
  152. HeaderReadCount = GetFileReadCount(Header);
  153. // Add BOM back
  154. RemapFile(Main,
  155. "\xef\xbb\xbf"
  156. "#include \"//./header.h\"\n"
  157. "int main() { return random() -2; }");
  158. ASSERT_TRUE(ReparseAST(AST));
  159. ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
  160. ASSERT_LE(HeaderReadCount, GetFileReadCount(Header));
  161. }
  162. } // anonymous namespace