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
28using namespace clang;
29
30namespace clang {
31class SourceManagerTestHelper {
32public:
33 static FileID makeFileID(int ID) { return FileID::get(V: ID); }
34};
35} // namespace clang
36
37namespace {
38
39// The test fixture.
40class SourceManagerTest : public ::testing::Test {
41protected:
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
61TEST_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
121TEST_F(SourceManagerTest, isInSystemHeader) {
122 // Check for invalid source location
123 SourceLocation LocEmpty;
124 bool isInSystemHeaderFalse = SourceMgr.isInSystemHeader(Loc: LocEmpty);
125 ASSERT_FALSE(isInSystemHeaderFalse);
126}
127
128TEST_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 HeaderInfo(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
174TEST_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 HeaderInfo(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
244TEST_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
294TEST_F(SourceManagerTest, locationPrintTest) {
295 const char *header = "#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> HeaderBuf =
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 HeaderFile = 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 HeaderFileID = 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 HeaderLoc = 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
342TEST_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.
384TEST_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
405struct 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
413TEST_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.
458TEST_F(SourceManagerTest, ResetsIncludeLocMap) {
459 auto ParseFile = [&] {
460 TrivialModuleLoader ModLoader;
461 HeaderSearchOptions HSOpts;
462 PreprocessorOptions PPOpts;
463 HeaderSearch HeaderInfo(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 HeaderFile =
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
512TEST_F(SourceManagerTest, getMacroArgExpandedLocation) {
513 const char *header =
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> HeaderBuf =
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 headerFile = 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 HeaderInfo(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
581namespace {
582
583struct 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
599class MacroTracker : public PPCallbacks {
600 std::vector<MacroAction> &Macros;
601
602public:
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
630TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithMacroInInclude) {
631 const char *header =
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> HeaderBuf =
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 headerFile = 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 HeaderInfo(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
723TEST_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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang/unittests/Basic/SourceManagerTest.cpp