brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.0 KiB · 9991838 Raw
274 lines · cpp
1//====-- unittests/Frontend/PCHPreambleTest.cpp - FrontendAction tests ---====//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//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 22using namespace llvm;23using namespace clang;24 25namespace {26 27std::string Canonicalize(const Twine &Path) {28  SmallVector<char, 128> PathVec;29  Path.toVector(PathVec);30  llvm::sys::path::remove_dots(PathVec, true);31  return std::string(PathVec.begin(), PathVec.end());32}33 34class ReadCountingInMemoryFileSystem : public vfs::InMemoryFileSystem35{36  std::map<std::string, unsigned> ReadCounts;37 38public:39  ErrorOr<std::unique_ptr<vfs::File>> openFileForRead(const Twine &Path) override40  {41    ++ReadCounts[Canonicalize(Path)];42    return InMemoryFileSystem::openFileForRead(Path);43  }44 45  unsigned GetReadCount(const Twine &Path) const46  {47    auto it = ReadCounts.find(Canonicalize(Path));48    return it == ReadCounts.end() ? 0 : it->second;49  }50};51 52class PCHPreambleTest : public ::testing::Test {53  IntrusiveRefCntPtr<ReadCountingInMemoryFileSystem> VFS;54  StringMap<std::string> RemappedFiles;55  std::shared_ptr<PCHContainerOperations> PCHContainerOpts;56  std::shared_ptr<DiagnosticOptions> DiagOpts =57      std::make_shared<DiagnosticOptions>();58  FileSystemOptions FSOpts;59 60public:61  void SetUp() override { ResetVFS(); }62  void TearDown() override {}63 64  void ResetVFS() {65    VFS = llvm::makeIntrusiveRefCnt<ReadCountingInMemoryFileSystem>();66    // We need the working directory to be set to something absolute,67    // otherwise it ends up being inadvertently set to the current68    // working directory in the real file system due to a series of69    // unfortunate conditions interacting badly.70    // What's more, this path *must* be absolute on all (real)71    // filesystems, so just '/' won't work (e.g. on Win32).72    VFS->setCurrentWorkingDirectory("//./");73  }74 75  void AddFile(const std::string &Filename, const std::string &Contents) {76    ::time_t now;77    ::time(&now);78    VFS->addFile(Filename, now, MemoryBuffer::getMemBufferCopy(Contents, Filename));79  }80 81  void RemapFile(const std::string &Filename, const std::string &Contents) {82    RemappedFiles[Filename] = Contents;83  }84 85  std::unique_ptr<ASTUnit> ParseAST(const std::string &EntryFile) {86    PCHContainerOpts = std::make_shared<PCHContainerOperations>();87    std::shared_ptr<CompilerInvocation> CI(new CompilerInvocation);88    CI->getFrontendOpts().Inputs.push_back(89      FrontendInputFile(EntryFile, FrontendOptions::getInputKindForExtension(90        llvm::sys::path::extension(EntryFile).substr(1))));91 92    CI->getTargetOpts().Triple = "i386-unknown-linux-gnu";93 94    CI->getPreprocessorOpts().RemappedFileBuffers = GetRemappedFiles();95 96    PreprocessorOptions &PPOpts = CI->getPreprocessorOpts();97    PPOpts.RemappedFilesKeepOriginalName = true;98 99    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(100        CompilerInstance::createDiagnostics(*VFS, *DiagOpts,101                                            new DiagnosticConsumer));102 103    auto FileMgr = llvm::makeIntrusiveRefCnt<FileManager>(FSOpts, VFS);104 105    std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(106        CI, PCHContainerOpts, DiagOpts, Diags, FileMgr, false,107        CaptureDiagsKind::None,108        /*PrecompilePreambleAfterNParses=*/1);109    return AST;110  }111 112  bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) {113    bool reparseFailed = AST->Reparse(PCHContainerOpts, GetRemappedFiles(), VFS);114    return !reparseFailed;115  }116 117  unsigned GetFileReadCount(const std::string &Filename) const {118    return VFS->GetReadCount(Filename);119  }120 121private:122  std::vector<std::pair<std::string, llvm::MemoryBuffer *>>123  GetRemappedFiles() const {124    std::vector<std::pair<std::string, llvm::MemoryBuffer *>> Remapped;125    for (const auto &RemappedFile : RemappedFiles) {126      std::unique_ptr<MemoryBuffer> buf = MemoryBuffer::getMemBufferCopy(127        RemappedFile.second, RemappedFile.first());128      Remapped.emplace_back(std::string(RemappedFile.first()), buf.release());129    }130    return Remapped;131  }132};133 134TEST_F(PCHPreambleTest, ReparseReusesPreambleWithUnsavedFileNotExistingOnDisk) {135  std::string Header1 = "//./header1.h";136  std::string MainName = "//./main.cpp";137  AddFile(MainName, R"cpp(138#include "//./header1.h"139int main() { return ZERO; }140)cpp");141  RemapFile(Header1, "#define ZERO 0\n");142 143  // Parse with header file provided as unsaved file, which does not exist on144  // disk.145  std::unique_ptr<ASTUnit> AST(ParseAST(MainName));146  ASSERT_TRUE(AST.get());147  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());148 149  // Reparse and check that the preamble was reused.150  ASSERT_TRUE(ReparseAST(AST));151  ASSERT_EQ(AST->getPreambleCounterForTests(), 1U);152}153 154TEST_F(PCHPreambleTest, ReparseReusesPreambleAfterUnsavedFileWasCreatedOnDisk) {155  std::string Header1 = "//./header1.h";156  std::string MainName = "//./main.cpp";157  AddFile(MainName, R"cpp(158#include "//./header1.h"159int main() { return ZERO; }160)cpp");161  RemapFile(Header1, "#define ZERO 0\n");162 163  // Parse with header file provided as unsaved file, which does not exist on164  // disk.165  std::unique_ptr<ASTUnit> AST(ParseAST(MainName));166  ASSERT_TRUE(AST.get());167  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());168 169  // Create the unsaved file also on disk and check that preamble was reused.170  AddFile(Header1, "#define ZERO 0\n");171  ASSERT_TRUE(ReparseAST(AST));172  ASSERT_EQ(AST->getPreambleCounterForTests(), 1U);173}174 175TEST_F(PCHPreambleTest,176       ReparseReusesPreambleAfterUnsavedFileWasRemovedFromDisk) {177  std::string Header1 = "//./foo/header1.h";178  std::string MainName = "//./main.cpp";179  std::string MainFileContent = R"cpp(180#include "//./foo/header1.h"181int main() { return ZERO; }182)cpp";183  AddFile(MainName, MainFileContent);184  AddFile(Header1, "#define ZERO 0\n");185  RemapFile(Header1, "#define ZERO 0\n");186 187  // Parse with header file provided as unsaved file, which exists on disk.188  std::unique_ptr<ASTUnit> AST(ParseAST(MainName));189  ASSERT_TRUE(AST.get());190  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());191  ASSERT_EQ(AST->getPreambleCounterForTests(), 1U);192 193  // Remove the unsaved file from disk and check that the preamble was reused.194  ResetVFS();195  AddFile(MainName, MainFileContent);196  ASSERT_TRUE(ReparseAST(AST));197  ASSERT_EQ(AST->getPreambleCounterForTests(), 1U);198}199 200TEST_F(PCHPreambleTest, ReparseWithOverriddenFileDoesNotInvalidatePreamble) {201  std::string Header1 = "//./header1.h";202  std::string Header2 = "//./header2.h";203  std::string MainName = "//./main.cpp";204  AddFile(Header1, "");205  AddFile(Header2, "#pragma once");206  AddFile(MainName,207    "#include \"//./foo/../header1.h\"\n"208    "#include \"//./foo/../header2.h\"\n"209    "int main() { return ZERO; }");210  RemapFile(Header1, "static const int ZERO = 0;\n");211 212  std::unique_ptr<ASTUnit> AST(ParseAST(MainName));213  ASSERT_TRUE(AST.get());214  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());215 216  unsigned initialCounts[] = {217    GetFileReadCount(MainName),218    GetFileReadCount(Header1),219    GetFileReadCount(Header2)220  };221 222  ASSERT_TRUE(ReparseAST(AST));223 224  ASSERT_NE(initialCounts[0], GetFileReadCount(MainName));225  ASSERT_EQ(initialCounts[1], GetFileReadCount(Header1));226  ASSERT_EQ(initialCounts[2], GetFileReadCount(Header2));227}228 229TEST_F(PCHPreambleTest, ParseWithBom) {230  std::string Header = "//./header.h";231  std::string Main = "//./main.cpp";232  AddFile(Header, "int random() { return 4; }");233  AddFile(Main,234    "\xef\xbb\xbf"235    "#include \"//./header.h\"\n"236    "int main() { return random() -2; }");237 238  std::unique_ptr<ASTUnit> AST(ParseAST(Main));239  ASSERT_TRUE(AST.get());240  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());241 242  unsigned HeaderReadCount = GetFileReadCount(Header);243 244  ASSERT_TRUE(ReparseAST(AST));245  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());246  247  // Check preamble PCH was really reused248  ASSERT_EQ(HeaderReadCount, GetFileReadCount(Header));249 250  // Remove BOM251  RemapFile(Main,252    "#include \"//./header.h\"\n"253    "int main() { return random() -2; }");254 255  ASSERT_TRUE(ReparseAST(AST));256  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());257 258  ASSERT_LE(HeaderReadCount, GetFileReadCount(Header));259  HeaderReadCount = GetFileReadCount(Header);260 261  // Add BOM back262  RemapFile(Main,263    "\xef\xbb\xbf"264    "#include \"//./header.h\"\n"265    "int main() { return random() -2; }");266 267  ASSERT_TRUE(ReparseAST(AST));268  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());269 270  ASSERT_LE(HeaderReadCount, GetFileReadCount(Header));271}272 273} // anonymous namespace274