1 | //===- unittests/Basic/SourceManagerTest.cpp ------ SourceManager 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/Basic/SourceManager.h" |
10 | #include "clang/Basic/Diagnostic.h" |
11 | #include "clang/Basic/DiagnosticOptions.h" |
12 | #include "clang/Basic/FileManager.h" |
13 | #include "clang/Basic/LangOptions.h" |
14 | #include "clang/Basic/TargetInfo.h" |
15 | #include "clang/Basic/TargetOptions.h" |
16 | #include "clang/Lex/HeaderSearch.h" |
17 | #include "clang/Lex/HeaderSearchOptions.h" |
18 | #include "clang/Lex/ModuleLoader.h" |
19 | #include "clang/Lex/Preprocessor.h" |
20 | #include "clang/Lex/PreprocessorOptions.h" |
21 | #include "llvm/ADT/SmallString.h" |
22 | #include "llvm/Config/llvm-config.h" |
23 | #include "llvm/Support/MemoryBuffer.h" |
24 | #include "llvm/Support/Process.h" |
25 | #include "gtest/gtest.h" |
26 | #include <cstddef> |
27 | |
28 | using namespace clang; |
29 | |
30 | namespace clang { |
31 | class SourceManagerTestHelper { |
32 | public: |
33 | static FileID makeFileID(int ID) { return FileID::get(V: ID); } |
34 | }; |
35 | } // namespace clang |
36 | |
37 | namespace { |
38 | |
39 | // The test fixture. |
40 | class SourceManagerTest : public ::testing::Test { |
41 | protected: |
42 | SourceManagerTest() |
43 | : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()), |
44 | Diags(DiagID, DiagOpts, new IgnoringDiagConsumer()), |
45 | SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions) { |
46 | TargetOpts->Triple = "x86_64-apple-darwin11.1.0" ; |
47 | Target = TargetInfo::CreateTargetInfo(Diags, Opts&: *TargetOpts); |
48 | } |
49 | |
50 | FileSystemOptions FileMgrOpts; |
51 | FileManager FileMgr; |
52 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID; |
53 | DiagnosticOptions DiagOpts; |
54 | DiagnosticsEngine Diags; |
55 | SourceManager SourceMgr; |
56 | LangOptions LangOpts; |
57 | std::shared_ptr<TargetOptions> TargetOpts; |
58 | IntrusiveRefCntPtr<TargetInfo> Target; |
59 | }; |
60 | |
61 | TEST_F(SourceManagerTest, isInMemoryBuffersNoSourceLocationInfo) { |
62 | // Check for invalid source location for each method |
63 | SourceLocation LocEmpty; |
64 | bool isWrittenInBuiltInFileFalse = SourceMgr.isWrittenInBuiltinFile(Loc: LocEmpty); |
65 | bool isWrittenInCommandLineFileFalse = |
66 | SourceMgr.isWrittenInCommandLineFile(Loc: LocEmpty); |
67 | bool isWrittenInScratchSpaceFalse = |
68 | SourceMgr.isWrittenInScratchSpace(Loc: LocEmpty); |
69 | |
70 | EXPECT_FALSE(isWrittenInBuiltInFileFalse); |
71 | EXPECT_FALSE(isWrittenInCommandLineFileFalse); |
72 | EXPECT_FALSE(isWrittenInScratchSpaceFalse); |
73 | |
74 | // Check for valid source location per filename for each method |
75 | const char *Source = "int x" ; |
76 | |
77 | std::unique_ptr<llvm::MemoryBuffer> BuiltInBuf = |
78 | llvm::MemoryBuffer::getMemBuffer(InputData: Source); |
79 | FileEntryRef BuiltInFile = |
80 | FileMgr.getVirtualFileRef(Filename: "<built-in>" , Size: BuiltInBuf->getBufferSize(), ModificationTime: 0); |
81 | SourceMgr.overrideFileContents(SourceFile: BuiltInFile, Buffer: std::move(BuiltInBuf)); |
82 | FileID BuiltInFileID = |
83 | SourceMgr.getOrCreateFileID(SourceFile: BuiltInFile, FileCharacter: SrcMgr::C_User); |
84 | SourceMgr.setMainFileID(BuiltInFileID); |
85 | SourceLocation LocBuiltIn = |
86 | SourceMgr.getLocForStartOfFile(FID: SourceMgr.getMainFileID()); |
87 | bool isWrittenInBuiltInFileTrue = |
88 | SourceMgr.isWrittenInBuiltinFile(Loc: LocBuiltIn); |
89 | |
90 | std::unique_ptr<llvm::MemoryBuffer> CommandLineBuf = |
91 | llvm::MemoryBuffer::getMemBuffer(InputData: Source); |
92 | FileEntryRef CommandLineFile = FileMgr.getVirtualFileRef( |
93 | Filename: "<command line>" , Size: CommandLineBuf->getBufferSize(), ModificationTime: 0); |
94 | SourceMgr.overrideFileContents(SourceFile: CommandLineFile, Buffer: std::move(CommandLineBuf)); |
95 | FileID CommandLineFileID = |
96 | SourceMgr.getOrCreateFileID(SourceFile: CommandLineFile, FileCharacter: SrcMgr::C_User); |
97 | SourceMgr.setMainFileID(CommandLineFileID); |
98 | SourceLocation LocCommandLine = |
99 | SourceMgr.getLocForStartOfFile(FID: SourceMgr.getMainFileID()); |
100 | bool isWrittenInCommandLineFileTrue = |
101 | SourceMgr.isWrittenInCommandLineFile(Loc: LocCommandLine); |
102 | |
103 | std::unique_ptr<llvm::MemoryBuffer> ScratchSpaceBuf = |
104 | llvm::MemoryBuffer::getMemBuffer(InputData: Source); |
105 | FileEntryRef ScratchSpaceFile = FileMgr.getVirtualFileRef( |
106 | Filename: "<scratch space>" , Size: ScratchSpaceBuf->getBufferSize(), ModificationTime: 0); |
107 | SourceMgr.overrideFileContents(SourceFile: ScratchSpaceFile, Buffer: std::move(ScratchSpaceBuf)); |
108 | FileID ScratchSpaceFileID = |
109 | SourceMgr.getOrCreateFileID(SourceFile: ScratchSpaceFile, FileCharacter: SrcMgr::C_User); |
110 | SourceMgr.setMainFileID(ScratchSpaceFileID); |
111 | SourceLocation LocScratchSpace = |
112 | SourceMgr.getLocForStartOfFile(FID: SourceMgr.getMainFileID()); |
113 | bool isWrittenInScratchSpaceTrue = |
114 | SourceMgr.isWrittenInScratchSpace(Loc: LocScratchSpace); |
115 | |
116 | EXPECT_TRUE(isWrittenInBuiltInFileTrue); |
117 | EXPECT_TRUE(isWrittenInCommandLineFileTrue); |
118 | EXPECT_TRUE(isWrittenInScratchSpaceTrue); |
119 | } |
120 | |
121 | TEST_F(SourceManagerTest, isInSystemHeader) { |
122 | // Check for invalid source location |
123 | SourceLocation LocEmpty; |
124 | bool = SourceMgr.isInSystemHeader(Loc: LocEmpty); |
125 | ASSERT_FALSE(isInSystemHeaderFalse); |
126 | } |
127 | |
128 | TEST_F(SourceManagerTest, isBeforeInTranslationUnit) { |
129 | const char *source = |
130 | "#define M(x) [x]\n" |
131 | "M(foo)" ; |
132 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
133 | llvm::MemoryBuffer::getMemBuffer(InputData: source); |
134 | FileID mainFileID = SourceMgr.createFileID(Buffer: std::move(Buf)); |
135 | SourceMgr.setMainFileID(mainFileID); |
136 | |
137 | HeaderSearchOptions HSOpts; |
138 | PreprocessorOptions PPOpts; |
139 | TrivialModuleLoader ModLoader; |
140 | HeaderSearch (HSOpts, SourceMgr, Diags, LangOpts, &*Target); |
141 | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, |
142 | /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); |
143 | PP.Initialize(Target: *Target); |
144 | PP.EnterMainSourceFile(); |
145 | |
146 | std::vector<Token> toks; |
147 | PP.LexTokensUntilEOF(Tokens: &toks); |
148 | |
149 | // Make sure we got the tokens that we expected. |
150 | ASSERT_EQ(3U, toks.size()); |
151 | ASSERT_EQ(tok::l_square, toks[0].getKind()); |
152 | ASSERT_EQ(tok::identifier, toks[1].getKind()); |
153 | ASSERT_EQ(tok::r_square, toks[2].getKind()); |
154 | |
155 | SourceLocation lsqrLoc = toks[0].getLocation(); |
156 | SourceLocation idLoc = toks[1].getLocation(); |
157 | SourceLocation rsqrLoc = toks[2].getLocation(); |
158 | |
159 | SourceLocation macroExpStartLoc = SourceMgr.translateLineCol(FID: mainFileID, Line: 2, Col: 1); |
160 | SourceLocation macroExpEndLoc = SourceMgr.translateLineCol(FID: mainFileID, Line: 2, Col: 6); |
161 | ASSERT_TRUE(macroExpStartLoc.isFileID()); |
162 | ASSERT_TRUE(macroExpEndLoc.isFileID()); |
163 | |
164 | SmallString<32> str; |
165 | ASSERT_EQ("M" , PP.getSpelling(macroExpStartLoc, str)); |
166 | ASSERT_EQ(")" , PP.getSpelling(macroExpEndLoc, str)); |
167 | |
168 | EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(lsqrLoc, idLoc)); |
169 | EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(idLoc, rsqrLoc)); |
170 | EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(macroExpStartLoc, idLoc)); |
171 | EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(idLoc, macroExpEndLoc)); |
172 | } |
173 | |
174 | TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithTokenSplit) { |
175 | const char *main = R"cpp( |
176 | #define ID(X) X |
177 | ID( |
178 | ID(a >> b) |
179 | c |
180 | ) |
181 | )cpp" ; |
182 | |
183 | SourceMgr.setMainFileID( |
184 | SourceMgr.createFileID(Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: main))); |
185 | |
186 | HeaderSearchOptions HSOpts; |
187 | PreprocessorOptions PPOpts; |
188 | TrivialModuleLoader ModLoader; |
189 | HeaderSearch (HSOpts, SourceMgr, Diags, LangOpts, &*Target); |
190 | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, |
191 | /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); |
192 | PP.Initialize(Target: *Target); |
193 | PP.EnterMainSourceFile(); |
194 | llvm::SmallString<8> Scratch; |
195 | |
196 | std::vector<Token> toks; |
197 | PP.LexTokensUntilEOF(Tokens: &toks); |
198 | |
199 | // Make sure we got the tokens that we expected. |
200 | ASSERT_EQ(4U, toks.size()) << "a >> b c" ; |
201 | // Sanity check their order. |
202 | for (unsigned I = 0; I < toks.size() - 1; ++I) { |
203 | EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(toks[I].getLocation(), |
204 | toks[I + 1].getLocation())); |
205 | EXPECT_FALSE(SourceMgr.isBeforeInTranslationUnit(toks[I + 1].getLocation(), |
206 | toks[I].getLocation())); |
207 | } |
208 | |
209 | // Split the >> into two > tokens, as happens when parsing nested templates. |
210 | unsigned RightShiftIndex = 1; |
211 | SourceLocation RightShift = toks[RightShiftIndex].getLocation(); |
212 | EXPECT_EQ(">>" , Lexer::getSpelling(SourceMgr.getSpellingLoc(RightShift), |
213 | Scratch, SourceMgr, LangOpts)); |
214 | SourceLocation Greater1 = PP.SplitToken(TokLoc: RightShift, /*Length=*/1); |
215 | SourceLocation Greater2 = RightShift.getLocWithOffset(Offset: 1); |
216 | EXPECT_TRUE(Greater1.isMacroID()); |
217 | EXPECT_EQ(">" , Lexer::getSpelling(SourceMgr.getSpellingLoc(Greater1), Scratch, |
218 | SourceMgr, LangOpts)); |
219 | EXPECT_EQ(">" , Lexer::getSpelling(SourceMgr.getSpellingLoc(Greater2), Scratch, |
220 | SourceMgr, LangOpts)); |
221 | EXPECT_EQ(SourceMgr.getImmediateExpansionRange(Greater1).getBegin(), |
222 | RightShift); |
223 | |
224 | for (unsigned I = 0; I < toks.size(); ++I) { |
225 | SCOPED_TRACE("Token " + std::to_string(I)); |
226 | // Right-shift is the parent of Greater1, so it compares less. |
227 | EXPECT_EQ( |
228 | SourceMgr.isBeforeInTranslationUnit(toks[I].getLocation(), Greater1), |
229 | I <= RightShiftIndex); |
230 | EXPECT_EQ( |
231 | SourceMgr.isBeforeInTranslationUnit(toks[I].getLocation(), Greater2), |
232 | I <= RightShiftIndex); |
233 | EXPECT_EQ( |
234 | SourceMgr.isBeforeInTranslationUnit(Greater1, toks[I].getLocation()), |
235 | RightShiftIndex < I); |
236 | EXPECT_EQ( |
237 | SourceMgr.isBeforeInTranslationUnit(Greater2, toks[I].getLocation()), |
238 | RightShiftIndex < I); |
239 | } |
240 | EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(Greater1, Greater2)); |
241 | EXPECT_FALSE(SourceMgr.isBeforeInTranslationUnit(Greater2, Greater1)); |
242 | } |
243 | |
244 | TEST_F(SourceManagerTest, getColumnNumber) { |
245 | const char *Source = |
246 | "int x;\n" |
247 | "int y;" ; |
248 | |
249 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
250 | llvm::MemoryBuffer::getMemBuffer(InputData: Source); |
251 | FileID MainFileID = SourceMgr.createFileID(Buffer: std::move(Buf)); |
252 | SourceMgr.setMainFileID(MainFileID); |
253 | |
254 | bool Invalid; |
255 | |
256 | Invalid = false; |
257 | EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 0, &Invalid)); |
258 | EXPECT_TRUE(!Invalid); |
259 | |
260 | Invalid = false; |
261 | EXPECT_EQ(5U, SourceMgr.getColumnNumber(MainFileID, 4, &Invalid)); |
262 | EXPECT_TRUE(!Invalid); |
263 | |
264 | Invalid = false; |
265 | EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 7, &Invalid)); |
266 | EXPECT_TRUE(!Invalid); |
267 | |
268 | Invalid = false; |
269 | EXPECT_EQ(5U, SourceMgr.getColumnNumber(MainFileID, 11, &Invalid)); |
270 | EXPECT_TRUE(!Invalid); |
271 | |
272 | Invalid = false; |
273 | EXPECT_EQ(7U, SourceMgr.getColumnNumber(MainFileID, strlen(Source), |
274 | &Invalid)); |
275 | EXPECT_TRUE(!Invalid); |
276 | |
277 | Invalid = false; |
278 | SourceMgr.getColumnNumber(FID: MainFileID, FilePos: strlen(s: Source)+1, Invalid: &Invalid); |
279 | EXPECT_TRUE(Invalid); |
280 | |
281 | // Test invalid files |
282 | Invalid = false; |
283 | SourceMgr.getColumnNumber(FID: FileID(), FilePos: 0, Invalid: &Invalid); |
284 | EXPECT_TRUE(Invalid); |
285 | |
286 | Invalid = false; |
287 | SourceMgr.getColumnNumber(FID: FileID(), FilePos: 1, Invalid: &Invalid); |
288 | EXPECT_TRUE(Invalid); |
289 | |
290 | // Test with no invalid flag. |
291 | EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 0, nullptr)); |
292 | } |
293 | |
294 | TEST_F(SourceManagerTest, locationPrintTest) { |
295 | const char * = "#define IDENTITY(x) x\n" ; |
296 | |
297 | const char *Source = "int x;\n" |
298 | "include \"test-header.h\"\n" |
299 | "IDENTITY(int y);\n" |
300 | "int z;" ; |
301 | |
302 | std::unique_ptr<llvm::MemoryBuffer> = |
303 | llvm::MemoryBuffer::getMemBuffer(InputData: header); |
304 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
305 | llvm::MemoryBuffer::getMemBuffer(InputData: Source); |
306 | |
307 | FileEntryRef SourceFile = |
308 | FileMgr.getVirtualFileRef(Filename: "/mainFile.cpp" , Size: Buf->getBufferSize(), ModificationTime: 0); |
309 | SourceMgr.overrideFileContents(SourceFile, Buffer: std::move(Buf)); |
310 | |
311 | FileEntryRef = FileMgr.getVirtualFileRef( |
312 | Filename: "/test-header.h" , Size: HeaderBuf->getBufferSize(), ModificationTime: 0); |
313 | SourceMgr.overrideFileContents(SourceFile: HeaderFile, Buffer: std::move(HeaderBuf)); |
314 | |
315 | FileID MainFileID = SourceMgr.getOrCreateFileID(SourceFile, FileCharacter: SrcMgr::C_User); |
316 | FileID = SourceMgr.getOrCreateFileID(SourceFile: HeaderFile, FileCharacter: SrcMgr::C_User); |
317 | SourceMgr.setMainFileID(MainFileID); |
318 | |
319 | auto BeginLoc = SourceMgr.getLocForStartOfFile(FID: MainFileID); |
320 | auto EndLoc = SourceMgr.getLocForEndOfFile(FID: MainFileID); |
321 | |
322 | auto BeginEOLLoc = SourceMgr.translateLineCol(FID: MainFileID, Line: 1, Col: 7); |
323 | |
324 | auto = SourceMgr.getLocForStartOfFile(FID: HeaderFileID); |
325 | |
326 | EXPECT_EQ(BeginLoc.printToString(SourceMgr), "/mainFile.cpp:1:1" ); |
327 | EXPECT_EQ(EndLoc.printToString(SourceMgr), "/mainFile.cpp:4:7" ); |
328 | |
329 | EXPECT_EQ(BeginEOLLoc.printToString(SourceMgr), "/mainFile.cpp:1:7" ); |
330 | EXPECT_EQ(HeaderLoc.printToString(SourceMgr), "/test-header.h:1:1" ); |
331 | |
332 | EXPECT_EQ(SourceRange(BeginLoc, BeginLoc).printToString(SourceMgr), |
333 | "</mainFile.cpp:1:1>" ); |
334 | EXPECT_EQ(SourceRange(BeginLoc, BeginEOLLoc).printToString(SourceMgr), |
335 | "</mainFile.cpp:1:1, col:7>" ); |
336 | EXPECT_EQ(SourceRange(BeginLoc, EndLoc).printToString(SourceMgr), |
337 | "</mainFile.cpp:1:1, line:4:7>" ); |
338 | EXPECT_EQ(SourceRange(BeginLoc, HeaderLoc).printToString(SourceMgr), |
339 | "</mainFile.cpp:1:1, /test-header.h:1:1>" ); |
340 | } |
341 | |
342 | TEST_F(SourceManagerTest, getInvalidBOM) { |
343 | ASSERT_EQ(SrcMgr::ContentCache::getInvalidBOM("" ), nullptr); |
344 | ASSERT_EQ(SrcMgr::ContentCache::getInvalidBOM("\x00\x00\x00" ), nullptr); |
345 | ASSERT_EQ(SrcMgr::ContentCache::getInvalidBOM("\xFF\xFF\xFF" ), nullptr); |
346 | ASSERT_EQ(SrcMgr::ContentCache::getInvalidBOM("#include <iostream>" ), |
347 | nullptr); |
348 | |
349 | ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM( |
350 | "\xFE\xFF#include <iostream>" )), |
351 | "UTF-16 (BE)" ); |
352 | ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM( |
353 | "\xFF\xFE#include <iostream>" )), |
354 | "UTF-16 (LE)" ); |
355 | ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM( |
356 | "\x2B\x2F\x76#include <iostream>" )), |
357 | "UTF-7" ); |
358 | ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM( |
359 | "\xF7\x64\x4C#include <iostream>" )), |
360 | "UTF-1" ); |
361 | ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM( |
362 | "\xDD\x73\x66\x73#include <iostream>" )), |
363 | "UTF-EBCDIC" ); |
364 | ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM( |
365 | "\x0E\xFE\xFF#include <iostream>" )), |
366 | "SCSU" ); |
367 | ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM( |
368 | "\xFB\xEE\x28#include <iostream>" )), |
369 | "BOCU-1" ); |
370 | ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM( |
371 | "\x84\x31\x95\x33#include <iostream>" )), |
372 | "GB-18030" ); |
373 | ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM( |
374 | llvm::StringLiteral::withInnerNUL( |
375 | "\x00\x00\xFE\xFF#include <iostream>" ))), |
376 | "UTF-32 (BE)" ); |
377 | ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM( |
378 | llvm::StringLiteral::withInnerNUL( |
379 | "\xFF\xFE\x00\x00#include <iostream>" ))), |
380 | "UTF-32 (LE)" ); |
381 | } |
382 | |
383 | // Regression test - there was an out of bound access for buffers not terminated by zero. |
384 | TEST_F(SourceManagerTest, getLineNumber) { |
385 | const unsigned pageSize = llvm::sys::Process::getPageSizeEstimate(); |
386 | std::unique_ptr<char[]> source(new char[pageSize]); |
387 | for(unsigned i = 0; i < pageSize; ++i) { |
388 | source[i] = 'a'; |
389 | } |
390 | |
391 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
392 | llvm::MemoryBuffer::getMemBuffer( |
393 | Ref: llvm::MemoryBufferRef( |
394 | llvm::StringRef(source.get(), 3), "whatever" |
395 | ), |
396 | RequiresNullTerminator: false |
397 | ); |
398 | |
399 | FileID mainFileID = SourceMgr.createFileID(Buffer: std::move(Buf)); |
400 | SourceMgr.setMainFileID(mainFileID); |
401 | |
402 | ASSERT_NO_FATAL_FAILURE(SourceMgr.getLineNumber(mainFileID, 1, nullptr)); |
403 | } |
404 | |
405 | struct FakeExternalSLocEntrySource : ExternalSLocEntrySource { |
406 | bool ReadSLocEntry(int ID) override { return {}; } |
407 | int getSLocEntryID(SourceLocation::UIntTy SLocOffset) override { return 0; } |
408 | std::pair<SourceLocation, StringRef> getModuleImportLoc(int ID) override { |
409 | return {}; |
410 | } |
411 | }; |
412 | |
413 | TEST_F(SourceManagerTest, loadedSLocEntryIsInTheSameTranslationUnit) { |
414 | auto InSameTU = [=](int LID, int RID) { |
415 | return SourceMgr.isInTheSameTranslationUnitImpl( |
416 | LOffs: std::make_pair(x: SourceManagerTestHelper::makeFileID(ID: LID), y: 0), |
417 | ROffs: std::make_pair(x: SourceManagerTestHelper::makeFileID(ID: RID), y: 0)); |
418 | }; |
419 | |
420 | FakeExternalSLocEntrySource ExternalSource; |
421 | SourceMgr.setExternalSLocEntrySource(&ExternalSource); |
422 | |
423 | unsigned ANumFileIDs = 10; |
424 | auto [AFirstID, X] = SourceMgr.AllocateLoadedSLocEntries(NumSLocEntries: ANumFileIDs, TotalSize: 10); |
425 | int ALastID = AFirstID + ANumFileIDs - 1; |
426 | // FileID(-11)..FileID(-2) |
427 | ASSERT_EQ(AFirstID, -11); |
428 | ASSERT_EQ(ALastID, -2); |
429 | |
430 | unsigned BNumFileIDs = 20; |
431 | auto [BFirstID, Y] = SourceMgr.AllocateLoadedSLocEntries(NumSLocEntries: BNumFileIDs, TotalSize: 20); |
432 | int BLastID = BFirstID + BNumFileIDs - 1; |
433 | // FileID(-31)..FileID(-12) |
434 | ASSERT_EQ(BFirstID, -31); |
435 | ASSERT_EQ(BLastID, -12); |
436 | |
437 | // Loaded vs local. |
438 | EXPECT_FALSE(InSameTU(-2, 1)); |
439 | |
440 | // Loaded in the same allocation A. |
441 | EXPECT_TRUE(InSameTU(-11, -2)); |
442 | EXPECT_TRUE(InSameTU(-11, -6)); |
443 | |
444 | // Loaded in the same allocation B. |
445 | EXPECT_TRUE(InSameTU(-31, -12)); |
446 | EXPECT_TRUE(InSameTU(-31, -16)); |
447 | |
448 | // Loaded from different allocations A and B. |
449 | EXPECT_FALSE(InSameTU(-12, -11)); |
450 | } |
451 | |
452 | #if defined(LLVM_ON_UNIX) |
453 | |
454 | // A single SourceManager instance is sometimes reused across multiple |
455 | // compilations. This test makes sure we're resetting caches built for tracking |
456 | // include locations that are based on FileIDs, to make sure we don't report |
457 | // wrong include locations when FileIDs coincide between two different runs. |
458 | TEST_F(SourceManagerTest, ResetsIncludeLocMap) { |
459 | auto ParseFile = [&] { |
460 | TrivialModuleLoader ModLoader; |
461 | HeaderSearchOptions HSOpts; |
462 | PreprocessorOptions PPOpts; |
463 | HeaderSearch (HSOpts, SourceMgr, Diags, LangOpts, &*Target); |
464 | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, |
465 | /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); |
466 | PP.Initialize(Target: *Target); |
467 | PP.EnterMainSourceFile(); |
468 | PP.LexTokensUntilEOF(); |
469 | EXPECT_FALSE(Diags.hasErrorOccurred()); |
470 | }; |
471 | |
472 | auto Buf = llvm::MemoryBuffer::getMemBuffer(InputData: "" ); |
473 | FileEntryRef = |
474 | FileMgr.getVirtualFileRef(Filename: "/foo.h" , Size: Buf->getBufferSize(), ModificationTime: 0); |
475 | SourceMgr.overrideFileContents(SourceFile: HeaderFile, Buffer: std::move(Buf)); |
476 | |
477 | Buf = llvm::MemoryBuffer::getMemBuffer(InputData: R"cpp(#include "/foo.h")cpp" ); |
478 | FileEntryRef BarFile = |
479 | FileMgr.getVirtualFileRef(Filename: "/bar.h" , Size: Buf->getBufferSize(), ModificationTime: 0); |
480 | SourceMgr.overrideFileContents(SourceFile: BarFile, Buffer: std::move(Buf)); |
481 | SourceMgr.createFileID(SourceFile: BarFile, IncludePos: {}, FileCharacter: clang::SrcMgr::C_User); |
482 | |
483 | Buf = llvm::MemoryBuffer::getMemBuffer(InputData: R"cpp(#include "/foo.h")cpp" ); |
484 | FileID MFID = SourceMgr.createFileID(Buffer: std::move(Buf)); |
485 | SourceMgr.setMainFileID(MFID); |
486 | |
487 | ParseFile(); |
488 | auto FooFID = SourceMgr.getOrCreateFileID(SourceFile: HeaderFile, FileCharacter: clang::SrcMgr::C_User); |
489 | auto IncFID = SourceMgr.getDecomposedIncludedLoc(FID: FooFID).first; |
490 | EXPECT_EQ(IncFID, MFID); |
491 | |
492 | // Clean up source-manager state before we start next parse. |
493 | SourceMgr.clearIDTables(); |
494 | |
495 | // Set up a new main file. |
496 | Buf = llvm::MemoryBuffer::getMemBuffer(InputData: R"cpp( |
497 | // silly comment 42 |
498 | #include "/bar.h")cpp" ); |
499 | MFID = SourceMgr.createFileID(Buffer: std::move(Buf)); |
500 | SourceMgr.setMainFileID(MFID); |
501 | |
502 | ParseFile(); |
503 | // Make sure foo.h got the same file-id in both runs. |
504 | EXPECT_EQ(FooFID, |
505 | SourceMgr.getOrCreateFileID(HeaderFile, clang::SrcMgr::C_User)); |
506 | auto BarFID = SourceMgr.getOrCreateFileID(SourceFile: BarFile, FileCharacter: clang::SrcMgr::C_User); |
507 | IncFID = SourceMgr.getDecomposedIncludedLoc(FID: FooFID).first; |
508 | // Check that includer is bar.h during this run. |
509 | EXPECT_EQ(IncFID, BarFID); |
510 | } |
511 | |
512 | TEST_F(SourceManagerTest, getMacroArgExpandedLocation) { |
513 | const char * = |
514 | "#define FM(x,y) x\n" ; |
515 | |
516 | const char *main = |
517 | "#include \"/test-header.h\"\n" |
518 | "#define VAL 0\n" |
519 | "FM(VAL,0)\n" |
520 | "FM(0,VAL)\n" |
521 | "FM(FM(0,VAL),0)\n" |
522 | "#define CONCAT(X, Y) X##Y\n" |
523 | "CONCAT(1,1)\n" ; |
524 | |
525 | std::unique_ptr<llvm::MemoryBuffer> = |
526 | llvm::MemoryBuffer::getMemBuffer(InputData: header); |
527 | std::unique_ptr<llvm::MemoryBuffer> MainBuf = |
528 | llvm::MemoryBuffer::getMemBuffer(InputData: main); |
529 | FileID mainFileID = SourceMgr.createFileID(Buffer: std::move(MainBuf)); |
530 | SourceMgr.setMainFileID(mainFileID); |
531 | |
532 | FileEntryRef = FileMgr.getVirtualFileRef( |
533 | Filename: "/test-header.h" , Size: HeaderBuf->getBufferSize(), ModificationTime: 0); |
534 | SourceMgr.overrideFileContents(SourceFile: headerFile, Buffer: std::move(HeaderBuf)); |
535 | |
536 | HeaderSearchOptions HSOpts; |
537 | PreprocessorOptions PPOpts; |
538 | TrivialModuleLoader ModLoader; |
539 | HeaderSearch (HSOpts, SourceMgr, Diags, LangOpts, &*Target); |
540 | |
541 | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, |
542 | /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); |
543 | // Ensure we can get expanded locations in presence of implicit includes. |
544 | // These are different than normal includes since predefines buffer doesn't |
545 | // have a valid insertion location. |
546 | PP.setPredefines("#include \"/implicit-header.h\"" ); |
547 | FileMgr.getVirtualFileRef(Filename: "/implicit-header.h" , Size: 0, ModificationTime: 0); |
548 | PP.Initialize(Target: *Target); |
549 | PP.EnterMainSourceFile(); |
550 | |
551 | std::vector<Token> toks; |
552 | PP.LexTokensUntilEOF(Tokens: &toks); |
553 | |
554 | // Make sure we got the tokens that we expected. |
555 | ASSERT_EQ(4U, toks.size()); |
556 | ASSERT_EQ(tok::numeric_constant, toks[0].getKind()); |
557 | ASSERT_EQ(tok::numeric_constant, toks[1].getKind()); |
558 | ASSERT_EQ(tok::numeric_constant, toks[2].getKind()); |
559 | ASSERT_EQ(tok::numeric_constant, toks[3].getKind()); |
560 | |
561 | SourceLocation defLoc = SourceMgr.translateLineCol(FID: mainFileID, Line: 2, Col: 13); |
562 | SourceLocation loc1 = SourceMgr.translateLineCol(FID: mainFileID, Line: 3, Col: 8); |
563 | SourceLocation loc2 = SourceMgr.translateLineCol(FID: mainFileID, Line: 4, Col: 4); |
564 | SourceLocation loc3 = SourceMgr.translateLineCol(FID: mainFileID, Line: 5, Col: 7); |
565 | SourceLocation defLoc2 = SourceMgr.translateLineCol(FID: mainFileID, Line: 6, Col: 22); |
566 | defLoc = SourceMgr.getMacroArgExpandedLocation(Loc: defLoc); |
567 | loc1 = SourceMgr.getMacroArgExpandedLocation(Loc: loc1); |
568 | loc2 = SourceMgr.getMacroArgExpandedLocation(Loc: loc2); |
569 | loc3 = SourceMgr.getMacroArgExpandedLocation(Loc: loc3); |
570 | defLoc2 = SourceMgr.getMacroArgExpandedLocation(Loc: defLoc2); |
571 | |
572 | EXPECT_TRUE(defLoc.isFileID()); |
573 | EXPECT_TRUE(loc1.isFileID()); |
574 | EXPECT_TRUE(SourceMgr.isMacroArgExpansion(loc2)); |
575 | EXPECT_TRUE(SourceMgr.isMacroArgExpansion(loc3)); |
576 | EXPECT_EQ(loc2, toks[1].getLocation()); |
577 | EXPECT_EQ(loc3, toks[2].getLocation()); |
578 | EXPECT_TRUE(defLoc2.isFileID()); |
579 | } |
580 | |
581 | namespace { |
582 | |
583 | struct MacroAction { |
584 | enum Kind { kExpansion, kDefinition, kUnDefinition}; |
585 | |
586 | SourceLocation Loc; |
587 | std::string Name; |
588 | LLVM_PREFERRED_TYPE(Kind) |
589 | unsigned MAKind : 3; |
590 | |
591 | MacroAction(SourceLocation Loc, StringRef Name, unsigned K) |
592 | : Loc(Loc), Name(std::string(Name)), MAKind(K) {} |
593 | |
594 | bool isExpansion() const { return MAKind == kExpansion; } |
595 | bool isDefinition() const { return MAKind & kDefinition; } |
596 | bool isUnDefinition() const { return MAKind & kUnDefinition; } |
597 | }; |
598 | |
599 | class MacroTracker : public PPCallbacks { |
600 | std::vector<MacroAction> &Macros; |
601 | |
602 | public: |
603 | explicit MacroTracker(std::vector<MacroAction> &Macros) : Macros(Macros) { } |
604 | |
605 | void MacroDefined(const Token &MacroNameTok, |
606 | const MacroDirective *MD) override { |
607 | Macros.push_back(x: MacroAction(MD->getLocation(), |
608 | MacroNameTok.getIdentifierInfo()->getName(), |
609 | MacroAction::kDefinition)); |
610 | } |
611 | void MacroUndefined(const Token &MacroNameTok, |
612 | const MacroDefinition &MD, |
613 | const MacroDirective *UD) override { |
614 | Macros.push_back( |
615 | x: MacroAction(UD ? UD->getLocation() : SourceLocation(), |
616 | MacroNameTok.getIdentifierInfo()->getName(), |
617 | UD ? MacroAction::kDefinition | MacroAction::kUnDefinition |
618 | : MacroAction::kUnDefinition)); |
619 | } |
620 | void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, |
621 | SourceRange Range, const MacroArgs *Args) override { |
622 | Macros.push_back(x: MacroAction(MacroNameTok.getLocation(), |
623 | MacroNameTok.getIdentifierInfo()->getName(), |
624 | MacroAction::kExpansion)); |
625 | } |
626 | }; |
627 | |
628 | } |
629 | |
630 | TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithMacroInInclude) { |
631 | const char * = |
632 | "#define MACRO_IN_INCLUDE 0\n" |
633 | "#define MACRO_DEFINED\n" |
634 | "#undef MACRO_DEFINED\n" |
635 | "#undef MACRO_UNDEFINED\n" ; |
636 | |
637 | const char *main = |
638 | "#define M(x) x\n" |
639 | "#define INC \"/test-header.h\"\n" |
640 | "#include M(INC)\n" |
641 | "#define INC2 </test-header.h>\n" |
642 | "#include M(INC2)\n" ; |
643 | |
644 | std::unique_ptr<llvm::MemoryBuffer> = |
645 | llvm::MemoryBuffer::getMemBuffer(InputData: header); |
646 | std::unique_ptr<llvm::MemoryBuffer> MainBuf = |
647 | llvm::MemoryBuffer::getMemBuffer(InputData: main); |
648 | SourceMgr.setMainFileID(SourceMgr.createFileID(Buffer: std::move(MainBuf))); |
649 | |
650 | FileEntryRef = FileMgr.getVirtualFileRef( |
651 | Filename: "/test-header.h" , Size: HeaderBuf->getBufferSize(), ModificationTime: 0); |
652 | SourceMgr.overrideFileContents(SourceFile: headerFile, Buffer: std::move(HeaderBuf)); |
653 | |
654 | HeaderSearchOptions HSOpts; |
655 | PreprocessorOptions PPOpts; |
656 | TrivialModuleLoader ModLoader; |
657 | HeaderSearch (HSOpts, SourceMgr, Diags, LangOpts, &*Target); |
658 | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, |
659 | /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); |
660 | PP.Initialize(Target: *Target); |
661 | |
662 | std::vector<MacroAction> Macros; |
663 | PP.addPPCallbacks(C: std::make_unique<MacroTracker>(args&: Macros)); |
664 | |
665 | PP.EnterMainSourceFile(); |
666 | |
667 | std::vector<Token> toks; |
668 | PP.LexTokensUntilEOF(Tokens: &toks); |
669 | |
670 | // Make sure we got the tokens that we expected. |
671 | ASSERT_EQ(0U, toks.size()); |
672 | |
673 | ASSERT_EQ(15U, Macros.size()); |
674 | // #define M(x) x |
675 | ASSERT_TRUE(Macros[0].isDefinition()); |
676 | ASSERT_EQ("M" , Macros[0].Name); |
677 | // #define INC "/test-header.h" |
678 | ASSERT_TRUE(Macros[1].isDefinition()); |
679 | ASSERT_EQ("INC" , Macros[1].Name); |
680 | // M expansion in #include M(INC) |
681 | ASSERT_FALSE(Macros[2].isDefinition()); |
682 | ASSERT_EQ("M" , Macros[2].Name); |
683 | // INC expansion in #include M(INC) |
684 | ASSERT_TRUE(Macros[3].isExpansion()); |
685 | ASSERT_EQ("INC" , Macros[3].Name); |
686 | // #define MACRO_IN_INCLUDE 0 |
687 | ASSERT_TRUE(Macros[4].isDefinition()); |
688 | ASSERT_EQ("MACRO_IN_INCLUDE" , Macros[4].Name); |
689 | // #define MACRO_DEFINED |
690 | ASSERT_TRUE(Macros[5].isDefinition()); |
691 | ASSERT_FALSE(Macros[5].isUnDefinition()); |
692 | ASSERT_EQ("MACRO_DEFINED" , Macros[5].Name); |
693 | // #undef MACRO_DEFINED |
694 | ASSERT_TRUE(Macros[6].isDefinition()); |
695 | ASSERT_TRUE(Macros[6].isUnDefinition()); |
696 | ASSERT_EQ("MACRO_DEFINED" , Macros[6].Name); |
697 | // #undef MACRO_UNDEFINED |
698 | ASSERT_FALSE(Macros[7].isDefinition()); |
699 | ASSERT_TRUE(Macros[7].isUnDefinition()); |
700 | ASSERT_EQ("MACRO_UNDEFINED" , Macros[7].Name); |
701 | // #define INC2 </test-header.h> |
702 | ASSERT_TRUE(Macros[8].isDefinition()); |
703 | ASSERT_EQ("INC2" , Macros[8].Name); |
704 | // M expansion in #include M(INC2) |
705 | ASSERT_FALSE(Macros[9].isDefinition()); |
706 | ASSERT_EQ("M" , Macros[9].Name); |
707 | // INC2 expansion in #include M(INC2) |
708 | ASSERT_TRUE(Macros[10].isExpansion()); |
709 | ASSERT_EQ("INC2" , Macros[10].Name); |
710 | // #define MACRO_IN_INCLUDE 0 |
711 | ASSERT_TRUE(Macros[11].isDefinition()); |
712 | ASSERT_EQ("MACRO_IN_INCLUDE" , Macros[11].Name); |
713 | |
714 | // The INC expansion in #include M(INC) comes before the first |
715 | // MACRO_IN_INCLUDE definition of the included file. |
716 | EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(Macros[3].Loc, Macros[4].Loc)); |
717 | |
718 | // The INC2 expansion in #include M(INC2) comes before the second |
719 | // MACRO_IN_INCLUDE definition of the included file. |
720 | EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(Macros[10].Loc, Macros[11].Loc)); |
721 | } |
722 | |
723 | TEST_F(SourceManagerTest, isMainFile) { |
724 | const char *Source = "int x;" ; |
725 | |
726 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
727 | llvm::MemoryBuffer::getMemBuffer(InputData: Source); |
728 | FileEntryRef SourceFile = |
729 | FileMgr.getVirtualFileRef(Filename: "mainFile.cpp" , Size: Buf->getBufferSize(), ModificationTime: 0); |
730 | SourceMgr.overrideFileContents(SourceFile, Buffer: std::move(Buf)); |
731 | |
732 | std::unique_ptr<llvm::MemoryBuffer> Buf2 = |
733 | llvm::MemoryBuffer::getMemBuffer(InputData: Source); |
734 | FileEntryRef SecondFile = |
735 | FileMgr.getVirtualFileRef(Filename: "file2.cpp" , Size: Buf2->getBufferSize(), ModificationTime: 0); |
736 | SourceMgr.overrideFileContents(SourceFile: SecondFile, Buffer: std::move(Buf2)); |
737 | |
738 | FileID MainFileID = SourceMgr.getOrCreateFileID(SourceFile, FileCharacter: SrcMgr::C_User); |
739 | SourceMgr.setMainFileID(MainFileID); |
740 | |
741 | EXPECT_TRUE(SourceMgr.isMainFile(*SourceFile)); |
742 | EXPECT_TRUE(SourceMgr.isMainFile(*SourceFile)); |
743 | EXPECT_FALSE(SourceMgr.isMainFile(*SecondFile)); |
744 | } |
745 | |
746 | #endif |
747 | |
748 | } // anonymous namespace |
749 | |