llvm-project

Форк
0
/
PCHPreambleTest.cpp 
269 строк · 8.8 Кб
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-exception
6
//
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

22
using namespace llvm;
23
using namespace clang;
24

25
namespace {
26

27
std::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

34
class ReadCountingInMemoryFileSystem : public vfs::InMemoryFileSystem
35
{
36
  std::map<std::string, unsigned> ReadCounts;
37

38
public:
39
  ErrorOr<std::unique_ptr<vfs::File>> openFileForRead(const Twine &Path) override
40
  {
41
    ++ReadCounts[Canonicalize(Path)];
42
    return InMemoryFileSystem::openFileForRead(Path);
43
  }
44

45
  unsigned GetReadCount(const Twine &Path) const
46
  {
47
    auto it = ReadCounts.find(Canonicalize(Path));
48
    return it == ReadCounts.end() ? 0 : it->second;
49
  }
50
};
51

52
class PCHPreambleTest : public ::testing::Test {
53
  IntrusiveRefCntPtr<ReadCountingInMemoryFileSystem> VFS;
54
  StringMap<std::string> RemappedFiles;
55
  std::shared_ptr<PCHContainerOperations> PCHContainerOpts;
56
  FileSystemOptions FSOpts;
57

58
public:
59
  void SetUp() override { ResetVFS(); }
60
  void TearDown() override {}
61

62
  void ResetVFS() {
63
    VFS = new ReadCountingInMemoryFileSystem();
64
    // We need the working directory to be set to something absolute,
65
    // otherwise it ends up being inadvertently set to the current
66
    // working directory in the real file system due to a series of
67
    // unfortunate conditions interacting badly.
68
    // What's more, this path *must* be absolute on all (real)
69
    // filesystems, so just '/' won't work (e.g. on Win32).
70
    VFS->setCurrentWorkingDirectory("//./");
71
  }
72

73
  void AddFile(const std::string &Filename, const std::string &Contents) {
74
    ::time_t now;
75
    ::time(&now);
76
    VFS->addFile(Filename, now, MemoryBuffer::getMemBufferCopy(Contents, Filename));
77
  }
78

79
  void RemapFile(const std::string &Filename, const std::string &Contents) {
80
    RemappedFiles[Filename] = Contents;
81
  }
82

83
  std::unique_ptr<ASTUnit> ParseAST(const std::string &EntryFile) {
84
    PCHContainerOpts = std::make_shared<PCHContainerOperations>();
85
    std::shared_ptr<CompilerInvocation> CI(new CompilerInvocation);
86
    CI->getFrontendOpts().Inputs.push_back(
87
      FrontendInputFile(EntryFile, FrontendOptions::getInputKindForExtension(
88
        llvm::sys::path::extension(EntryFile).substr(1))));
89

90
    CI->getTargetOpts().Triple = "i386-unknown-linux-gnu";
91

92
    CI->getPreprocessorOpts().RemappedFileBuffers = GetRemappedFiles();
93

94
    PreprocessorOptions &PPOpts = CI->getPreprocessorOpts();
95
    PPOpts.RemappedFilesKeepOriginalName = true;
96

97
    IntrusiveRefCntPtr<DiagnosticsEngine>
98
      Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions, new DiagnosticConsumer));
99

100
    FileManager *FileMgr = new FileManager(FSOpts, VFS);
101

102
    std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
103
        CI, PCHContainerOpts, Diags, FileMgr, false, CaptureDiagsKind::None,
104
        /*PrecompilePreambleAfterNParses=*/1);
105
    return AST;
106
  }
107

108
  bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) {
109
    bool reparseFailed = AST->Reparse(PCHContainerOpts, GetRemappedFiles(), VFS);
110
    return !reparseFailed;
111
  }
112

113
  unsigned GetFileReadCount(const std::string &Filename) const {
114
    return VFS->GetReadCount(Filename);
115
  }
116

117
private:
118
  std::vector<std::pair<std::string, llvm::MemoryBuffer *>>
119
  GetRemappedFiles() const {
120
    std::vector<std::pair<std::string, llvm::MemoryBuffer *>> Remapped;
121
    for (const auto &RemappedFile : RemappedFiles) {
122
      std::unique_ptr<MemoryBuffer> buf = MemoryBuffer::getMemBufferCopy(
123
        RemappedFile.second, RemappedFile.first());
124
      Remapped.emplace_back(std::string(RemappedFile.first()), buf.release());
125
    }
126
    return Remapped;
127
  }
128
};
129

130
TEST_F(PCHPreambleTest, ReparseReusesPreambleWithUnsavedFileNotExistingOnDisk) {
131
  std::string Header1 = "//./header1.h";
132
  std::string MainName = "//./main.cpp";
133
  AddFile(MainName, R"cpp(
134
#include "//./header1.h"
135
int main() { return ZERO; }
136
)cpp");
137
  RemapFile(Header1, "#define ZERO 0\n");
138

139
  // Parse with header file provided as unsaved file, which does not exist on
140
  // disk.
141
  std::unique_ptr<ASTUnit> AST(ParseAST(MainName));
142
  ASSERT_TRUE(AST.get());
143
  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
144

145
  // Reparse and check that the preamble was reused.
146
  ASSERT_TRUE(ReparseAST(AST));
147
  ASSERT_EQ(AST->getPreambleCounterForTests(), 1U);
148
}
149

150
TEST_F(PCHPreambleTest, ReparseReusesPreambleAfterUnsavedFileWasCreatedOnDisk) {
151
  std::string Header1 = "//./header1.h";
152
  std::string MainName = "//./main.cpp";
153
  AddFile(MainName, R"cpp(
154
#include "//./header1.h"
155
int main() { return ZERO; }
156
)cpp");
157
  RemapFile(Header1, "#define ZERO 0\n");
158

159
  // Parse with header file provided as unsaved file, which does not exist on
160
  // disk.
161
  std::unique_ptr<ASTUnit> AST(ParseAST(MainName));
162
  ASSERT_TRUE(AST.get());
163
  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
164

165
  // Create the unsaved file also on disk and check that preamble was reused.
166
  AddFile(Header1, "#define ZERO 0\n");
167
  ASSERT_TRUE(ReparseAST(AST));
168
  ASSERT_EQ(AST->getPreambleCounterForTests(), 1U);
169
}
170

171
TEST_F(PCHPreambleTest,
172
       ReparseReusesPreambleAfterUnsavedFileWasRemovedFromDisk) {
173
  std::string Header1 = "//./foo/header1.h";
174
  std::string MainName = "//./main.cpp";
175
  std::string MainFileContent = R"cpp(
176
#include "//./foo/header1.h"
177
int main() { return ZERO; }
178
)cpp";
179
  AddFile(MainName, MainFileContent);
180
  AddFile(Header1, "#define ZERO 0\n");
181
  RemapFile(Header1, "#define ZERO 0\n");
182

183
  // Parse with header file provided as unsaved file, which exists on disk.
184
  std::unique_ptr<ASTUnit> AST(ParseAST(MainName));
185
  ASSERT_TRUE(AST.get());
186
  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
187
  ASSERT_EQ(AST->getPreambleCounterForTests(), 1U);
188

189
  // Remove the unsaved file from disk and check that the preamble was reused.
190
  ResetVFS();
191
  AddFile(MainName, MainFileContent);
192
  ASSERT_TRUE(ReparseAST(AST));
193
  ASSERT_EQ(AST->getPreambleCounterForTests(), 1U);
194
}
195

196
TEST_F(PCHPreambleTest, ReparseWithOverriddenFileDoesNotInvalidatePreamble) {
197
  std::string Header1 = "//./header1.h";
198
  std::string Header2 = "//./header2.h";
199
  std::string MainName = "//./main.cpp";
200
  AddFile(Header1, "");
201
  AddFile(Header2, "#pragma once");
202
  AddFile(MainName,
203
    "#include \"//./foo/../header1.h\"\n"
204
    "#include \"//./foo/../header2.h\"\n"
205
    "int main() { return ZERO; }");
206
  RemapFile(Header1, "static const int ZERO = 0;\n");
207

208
  std::unique_ptr<ASTUnit> AST(ParseAST(MainName));
209
  ASSERT_TRUE(AST.get());
210
  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
211

212
  unsigned initialCounts[] = {
213
    GetFileReadCount(MainName),
214
    GetFileReadCount(Header1),
215
    GetFileReadCount(Header2)
216
  };
217

218
  ASSERT_TRUE(ReparseAST(AST));
219

220
  ASSERT_NE(initialCounts[0], GetFileReadCount(MainName));
221
  ASSERT_EQ(initialCounts[1], GetFileReadCount(Header1));
222
  ASSERT_EQ(initialCounts[2], GetFileReadCount(Header2));
223
}
224

225
TEST_F(PCHPreambleTest, ParseWithBom) {
226
  std::string Header = "//./header.h";
227
  std::string Main = "//./main.cpp";
228
  AddFile(Header, "int random() { return 4; }");
229
  AddFile(Main,
230
    "\xef\xbb\xbf"
231
    "#include \"//./header.h\"\n"
232
    "int main() { return random() -2; }");
233

234
  std::unique_ptr<ASTUnit> AST(ParseAST(Main));
235
  ASSERT_TRUE(AST.get());
236
  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
237

238
  unsigned HeaderReadCount = GetFileReadCount(Header);
239

240
  ASSERT_TRUE(ReparseAST(AST));
241
  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
242
  
243
  // Check preamble PCH was really reused
244
  ASSERT_EQ(HeaderReadCount, GetFileReadCount(Header));
245

246
  // Remove BOM
247
  RemapFile(Main,
248
    "#include \"//./header.h\"\n"
249
    "int main() { return random() -2; }");
250

251
  ASSERT_TRUE(ReparseAST(AST));
252
  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
253

254
  ASSERT_LE(HeaderReadCount, GetFileReadCount(Header));
255
  HeaderReadCount = GetFileReadCount(Header);
256

257
  // Add BOM back
258
  RemapFile(Main,
259
    "\xef\xbb\xbf"
260
    "#include \"//./header.h\"\n"
261
    "int main() { return random() -2; }");
262

263
  ASSERT_TRUE(ReparseAST(AST));
264
  ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred());
265

266
  ASSERT_LE(HeaderReadCount, GetFileReadCount(Header));
267
}
268

269
} // anonymous namespace
270

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.