1 | |
2 | |
3 | |
4 | |
5 | |
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 | class ReadCountingInMemoryFileSystem : public vfs::InMemoryFileSystem |
28 | { |
29 | std::map<std::string, unsigned> ReadCounts; |
30 | |
31 | public: |
32 | ErrorOr<std::unique_ptr<vfs::File>> openFileForRead(const Twine &Path) override |
33 | { |
34 | SmallVector<char, 128> PathVec; |
35 | Path.toVector(PathVec); |
36 | llvm::sys::path::remove_dots(PathVec, true); |
37 | ++ReadCounts[std::string(PathVec.begin(), PathVec.end())]; |
38 | return InMemoryFileSystem::openFileForRead(Path); |
39 | } |
40 | |
41 | unsigned GetReadCount(const Twine &Path) const |
42 | { |
43 | auto it = ReadCounts.find(Path.str()); |
44 | return it == ReadCounts.end() ? 0 : it->second; |
45 | } |
46 | }; |
47 | |
48 | class PCHPreambleTest : public ::testing::Test { |
49 | IntrusiveRefCntPtr<ReadCountingInMemoryFileSystem> VFS; |
50 | StringMap<std::string> RemappedFiles; |
51 | std::shared_ptr<PCHContainerOperations> PCHContainerOpts; |
52 | FileSystemOptions FSOpts; |
53 | |
54 | public: |
55 | void SetUp() override { |
56 | VFS = new ReadCountingInMemoryFileSystem(); |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | |
63 | VFS->setCurrentWorkingDirectory("//./"); |
64 | } |
65 | |
66 | void TearDown() override { |
67 | } |
68 | |
69 | void AddFile(const std::string &Filename, const std::string &Contents) { |
70 | ::time_t now; |
71 | ::time(&now); |
72 | VFS->addFile(Filename, now, MemoryBuffer::getMemBufferCopy(Contents, Filename)); |
73 | } |
74 | |
75 | void RemapFile(const std::string &Filename, const std::string &Contents) { |
76 | RemappedFiles[Filename] = Contents; |
77 | } |
78 | |
79 | std::unique_ptr<ASTUnit> ParseAST(const std::string &EntryFile) { |
80 | PCHContainerOpts = std::make_shared<PCHContainerOperations>(); |
81 | std::shared_ptr<CompilerInvocation> CI(new CompilerInvocation); |
82 | CI->getFrontendOpts().Inputs.push_back( |
83 | FrontendInputFile(EntryFile, FrontendOptions::getInputKindForExtension( |
84 | llvm::sys::path::extension(EntryFile).substr(1)))); |
85 | |
86 | CI->getTargetOpts().Triple = "i386-unknown-linux-gnu"; |
87 | |
88 | CI->getPreprocessorOpts().RemappedFileBuffers = GetRemappedFiles(); |
89 | |
90 | PreprocessorOptions &PPOpts = CI->getPreprocessorOpts(); |
91 | PPOpts.RemappedFilesKeepOriginalName = true; |
92 | |
93 | IntrusiveRefCntPtr<DiagnosticsEngine> |
94 | Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions, new DiagnosticConsumer)); |
95 | |
96 | FileManager *FileMgr = new FileManager(FSOpts, VFS); |
97 | |
98 | std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation( |
99 | CI, PCHContainerOpts, Diags, FileMgr, false, false, |
100 | ); |
101 | return AST; |
102 | } |
103 | |
104 | bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) { |
105 | bool reparseFailed = AST->Reparse(PCHContainerOpts, GetRemappedFiles(), VFS); |
106 | return !reparseFailed; |
107 | } |
108 | |
109 | unsigned GetFileReadCount(const std::string &Filename) const { |
110 | return VFS->GetReadCount(Filename); |
111 | } |
112 | |
113 | private: |
114 | std::vector<std::pair<std::string, llvm::MemoryBuffer *>> |
115 | GetRemappedFiles() const { |
116 | std::vector<std::pair<std::string, llvm::MemoryBuffer *>> Remapped; |
117 | for (const auto &RemappedFile : RemappedFiles) { |
118 | std::unique_ptr<MemoryBuffer> buf = MemoryBuffer::getMemBufferCopy( |
119 | RemappedFile.second, RemappedFile.first()); |
120 | Remapped.emplace_back(RemappedFile.first(), buf.release()); |
121 | } |
122 | return Remapped; |
123 | } |
124 | }; |
125 | |
126 | TEST_F(PCHPreambleTest, ReparseWithOverriddenFileDoesNotInvalidatePreamble) { |
127 | std::string = "//./header1.h"; |
128 | std::string = "//./header2.h"; |
129 | std::string MainName = "//./main.cpp"; |
130 | AddFile(Header1, ""); |
131 | AddFile(Header2, "#pragma once"); |
132 | AddFile(MainName, |
133 | "#include \"//./foo/../header1.h\"\n" |
134 | "#include \"//./foo/../header2.h\"\n" |
135 | "int main() { return ZERO; }"); |
136 | RemapFile(Header1, "static const int ZERO = 0;\n"); |
137 | |
138 | std::unique_ptr<ASTUnit> AST(ParseAST(MainName)); |
139 | ASSERT_TRUE(AST.get()); |
140 | ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
141 | |
142 | unsigned initialCounts[] = { |
143 | GetFileReadCount(MainName), |
144 | GetFileReadCount(Header1), |
145 | GetFileReadCount(Header2) |
146 | }; |
147 | |
148 | ASSERT_TRUE(ReparseAST(AST)); |
149 | |
150 | ASSERT_NE(initialCounts[0], GetFileReadCount(MainName)); |
151 | ASSERT_EQ(initialCounts[1], GetFileReadCount(Header1)); |
152 | ASSERT_EQ(initialCounts[2], GetFileReadCount(Header2)); |
153 | } |
154 | |
155 | TEST_F(PCHPreambleTest, ParseWithBom) { |
156 | std::string = "//./header.h"; |
157 | std::string Main = "//./main.cpp"; |
158 | AddFile(Header, "int random() { return 4; }"); |
159 | AddFile(Main, |
160 | "\xef\xbb\xbf" |
161 | "#include \"//./header.h\"\n" |
162 | "int main() { return random() -2; }"); |
163 | |
164 | std::unique_ptr<ASTUnit> AST(ParseAST(Main)); |
165 | ASSERT_TRUE(AST.get()); |
166 | ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
167 | |
168 | unsigned = GetFileReadCount(Header); |
169 | |
170 | ASSERT_TRUE(ReparseAST(AST)); |
171 | ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
172 | |
173 | |
174 | ASSERT_EQ(HeaderReadCount, GetFileReadCount(Header)); |
175 | |
176 | |
177 | RemapFile(Main, |
178 | "#include \"//./header.h\"\n" |
179 | "int main() { return random() -2; }"); |
180 | |
181 | ASSERT_TRUE(ReparseAST(AST)); |
182 | ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
183 | |
184 | ASSERT_LE(HeaderReadCount, GetFileReadCount(Header)); |
185 | HeaderReadCount = GetFileReadCount(Header); |
186 | |
187 | |
188 | RemapFile(Main, |
189 | "\xef\xbb\xbf" |
190 | "#include \"//./header.h\"\n" |
191 | "int main() { return random() -2; }"); |
192 | |
193 | ASSERT_TRUE(ReparseAST(AST)); |
194 | ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
195 | |
196 | ASSERT_LE(HeaderReadCount, GetFileReadCount(Header)); |
197 | } |
198 | |
199 | } |
200 | |