xref: /llvm-project/clang/unittests/Basic/SourceManagerTest.cpp (revision 8fa5e98fd191d02fc7e0e220d74af267b9140e6a)
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 "gtest/gtest.h"
24 
25 using namespace clang;
26 
27 namespace {
28 
29 // The test fixture.
30 class SourceManagerTest : public ::testing::Test {
31 protected:
32   SourceManagerTest()
33     : FileMgr(FileMgrOpts),
34       DiagID(new DiagnosticIDs()),
35       Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
36       SourceMgr(Diags, FileMgr),
37       TargetOpts(new TargetOptions) {
38     TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
39     Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
40   }
41 
42   FileSystemOptions FileMgrOpts;
43   FileManager FileMgr;
44   IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
45   DiagnosticsEngine Diags;
46   SourceManager SourceMgr;
47   LangOptions LangOpts;
48   std::shared_ptr<TargetOptions> TargetOpts;
49   IntrusiveRefCntPtr<TargetInfo> Target;
50 };
51 
52 TEST_F(SourceManagerTest, isBeforeInTranslationUnit) {
53   const char *source =
54     "#define M(x) [x]\n"
55     "M(foo)";
56   std::unique_ptr<llvm::MemoryBuffer> Buf =
57       llvm::MemoryBuffer::getMemBuffer(source);
58   FileID mainFileID = SourceMgr.createFileID(std::move(Buf));
59   SourceMgr.setMainFileID(mainFileID);
60 
61   TrivialModuleLoader ModLoader;
62   HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
63                           Diags, LangOpts, &*Target);
64   Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
65                   SourceMgr, HeaderInfo, ModLoader,
66                   /*IILookup =*/nullptr,
67                   /*OwnsHeaderSearch =*/false);
68   PP.Initialize(*Target);
69   PP.EnterMainSourceFile();
70 
71   std::vector<Token> toks;
72   while (1) {
73     Token tok;
74     PP.Lex(tok);
75     if (tok.is(tok::eof))
76       break;
77     toks.push_back(tok);
78   }
79 
80   // Make sure we got the tokens that we expected.
81   ASSERT_EQ(3U, toks.size());
82   ASSERT_EQ(tok::l_square, toks[0].getKind());
83   ASSERT_EQ(tok::identifier, toks[1].getKind());
84   ASSERT_EQ(tok::r_square, toks[2].getKind());
85 
86   SourceLocation lsqrLoc = toks[0].getLocation();
87   SourceLocation idLoc = toks[1].getLocation();
88   SourceLocation rsqrLoc = toks[2].getLocation();
89 
90   SourceLocation macroExpStartLoc = SourceMgr.translateLineCol(mainFileID, 2, 1);
91   SourceLocation macroExpEndLoc = SourceMgr.translateLineCol(mainFileID, 2, 6);
92   ASSERT_TRUE(macroExpStartLoc.isFileID());
93   ASSERT_TRUE(macroExpEndLoc.isFileID());
94 
95   SmallString<32> str;
96   ASSERT_EQ("M", PP.getSpelling(macroExpStartLoc, str));
97   ASSERT_EQ(")", PP.getSpelling(macroExpEndLoc, str));
98 
99   EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(lsqrLoc, idLoc));
100   EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(idLoc, rsqrLoc));
101   EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(macroExpStartLoc, idLoc));
102   EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(idLoc, macroExpEndLoc));
103 }
104 
105 TEST_F(SourceManagerTest, getColumnNumber) {
106   const char *Source =
107     "int x;\n"
108     "int y;";
109 
110   std::unique_ptr<llvm::MemoryBuffer> Buf =
111       llvm::MemoryBuffer::getMemBuffer(Source);
112   FileID MainFileID = SourceMgr.createFileID(std::move(Buf));
113   SourceMgr.setMainFileID(MainFileID);
114 
115   bool Invalid;
116 
117   Invalid = false;
118   EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 0, &Invalid));
119   EXPECT_TRUE(!Invalid);
120 
121   Invalid = false;
122   EXPECT_EQ(5U, SourceMgr.getColumnNumber(MainFileID, 4, &Invalid));
123   EXPECT_TRUE(!Invalid);
124 
125   Invalid = false;
126   EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 7, &Invalid));
127   EXPECT_TRUE(!Invalid);
128 
129   Invalid = false;
130   EXPECT_EQ(5U, SourceMgr.getColumnNumber(MainFileID, 11, &Invalid));
131   EXPECT_TRUE(!Invalid);
132 
133   Invalid = false;
134   EXPECT_EQ(7U, SourceMgr.getColumnNumber(MainFileID, strlen(Source),
135                                          &Invalid));
136   EXPECT_TRUE(!Invalid);
137 
138   Invalid = false;
139   SourceMgr.getColumnNumber(MainFileID, strlen(Source)+1, &Invalid);
140   EXPECT_TRUE(Invalid);
141 
142   // Test invalid files
143   Invalid = false;
144   SourceMgr.getColumnNumber(FileID(), 0, &Invalid);
145   EXPECT_TRUE(Invalid);
146 
147   Invalid = false;
148   SourceMgr.getColumnNumber(FileID(), 1, &Invalid);
149   EXPECT_TRUE(Invalid);
150 
151   // Test with no invalid flag.
152   EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 0, nullptr));
153 }
154 
155 TEST_F(SourceManagerTest, locationPrintTest) {
156   const char *header = "#define IDENTITY(x) x\n";
157 
158   const char *Source = "int x;\n"
159                        "include \"test-header.h\"\n"
160                        "IDENTITY(int y);\n"
161                        "int z;";
162 
163   std::unique_ptr<llvm::MemoryBuffer> HeaderBuf =
164       llvm::MemoryBuffer::getMemBuffer(header);
165   std::unique_ptr<llvm::MemoryBuffer> Buf =
166       llvm::MemoryBuffer::getMemBuffer(Source);
167 
168   const FileEntry *SourceFile =
169       FileMgr.getVirtualFile("/mainFile.cpp", Buf->getBufferSize(), 0);
170   SourceMgr.overrideFileContents(SourceFile, std::move(Buf));
171 
172   const FileEntry *HeaderFile =
173       FileMgr.getVirtualFile("/test-header.h", HeaderBuf->getBufferSize(), 0);
174   SourceMgr.overrideFileContents(HeaderFile, std::move(HeaderBuf));
175 
176   FileID MainFileID = SourceMgr.getOrCreateFileID(SourceFile, SrcMgr::C_User);
177   FileID HeaderFileID = SourceMgr.getOrCreateFileID(HeaderFile, SrcMgr::C_User);
178   SourceMgr.setMainFileID(MainFileID);
179 
180   auto BeginLoc = SourceMgr.getLocForStartOfFile(MainFileID);
181   auto EndLoc = SourceMgr.getLocForEndOfFile(MainFileID);
182 
183   auto BeginEOLLoc = SourceMgr.translateLineCol(MainFileID, 1, 7);
184 
185   auto HeaderLoc = SourceMgr.getLocForStartOfFile(HeaderFileID);
186 
187   EXPECT_EQ(BeginLoc.printToString(SourceMgr), "/mainFile.cpp:1:1");
188   EXPECT_EQ(EndLoc.printToString(SourceMgr), "/mainFile.cpp:4:7");
189 
190   EXPECT_EQ(BeginEOLLoc.printToString(SourceMgr), "/mainFile.cpp:1:7");
191   EXPECT_EQ(HeaderLoc.printToString(SourceMgr), "/test-header.h:1:1");
192 
193   EXPECT_EQ(SourceRange(BeginLoc, BeginLoc).printToString(SourceMgr),
194             "</mainFile.cpp:1:1>");
195   EXPECT_EQ(SourceRange(BeginLoc, BeginEOLLoc).printToString(SourceMgr),
196             "</mainFile.cpp:1:1, col:7>");
197   EXPECT_EQ(SourceRange(BeginLoc, EndLoc).printToString(SourceMgr),
198             "</mainFile.cpp:1:1, line:4:7>");
199   EXPECT_EQ(SourceRange(BeginLoc, HeaderLoc).printToString(SourceMgr),
200             "</mainFile.cpp:1:1, /test-header.h:1:1>");
201 }
202 
203 TEST_F(SourceManagerTest, getInvalidBOM) {
204   ASSERT_EQ(SrcMgr::ContentCache::getInvalidBOM(""), nullptr);
205   ASSERT_EQ(SrcMgr::ContentCache::getInvalidBOM("\x00\x00\x00"), nullptr);
206   ASSERT_EQ(SrcMgr::ContentCache::getInvalidBOM("\xFF\xFF\xFF"), nullptr);
207   ASSERT_EQ(SrcMgr::ContentCache::getInvalidBOM("#include <iostream>"),
208             nullptr);
209 
210   ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM(
211                 "\xFE\xFF#include <iostream>")),
212             "UTF-16 (BE)");
213   ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM(
214                 "\xFF\xFE#include <iostream>")),
215             "UTF-16 (LE)");
216   ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM(
217                 "\x2B\x2F\x76#include <iostream>")),
218             "UTF-7");
219   ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM(
220                 "\xF7\x64\x4C#include <iostream>")),
221             "UTF-1");
222   ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM(
223                 "\xDD\x73\x66\x73#include <iostream>")),
224             "UTF-EBCDIC");
225   ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM(
226                 "\x0E\xFE\xFF#include <iostream>")),
227             "SCSU");
228   ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM(
229                 "\xFB\xEE\x28#include <iostream>")),
230             "BOCU-1");
231   ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM(
232                 "\x84\x31\x95\x33#include <iostream>")),
233             "GB-18030");
234   ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM(
235                 llvm::StringLiteral::withInnerNUL(
236                     "\x00\x00\xFE\xFF#include <iostream>"))),
237             "UTF-32 (BE)");
238   ASSERT_EQ(StringRef(SrcMgr::ContentCache::getInvalidBOM(
239                 llvm::StringLiteral::withInnerNUL(
240                     "\xFF\xFE\x00\x00#include <iostream>"))),
241             "UTF-32 (LE)");
242 }
243 
244 #if defined(LLVM_ON_UNIX)
245 
246 TEST_F(SourceManagerTest, getMacroArgExpandedLocation) {
247   const char *header =
248     "#define FM(x,y) x\n";
249 
250   const char *main =
251     "#include \"/test-header.h\"\n"
252     "#define VAL 0\n"
253     "FM(VAL,0)\n"
254     "FM(0,VAL)\n"
255     "FM(FM(0,VAL),0)\n"
256     "#define CONCAT(X, Y) X##Y\n"
257     "CONCAT(1,1)\n";
258 
259   std::unique_ptr<llvm::MemoryBuffer> HeaderBuf =
260       llvm::MemoryBuffer::getMemBuffer(header);
261   std::unique_ptr<llvm::MemoryBuffer> MainBuf =
262       llvm::MemoryBuffer::getMemBuffer(main);
263   FileID mainFileID = SourceMgr.createFileID(std::move(MainBuf));
264   SourceMgr.setMainFileID(mainFileID);
265 
266   const FileEntry *headerFile = FileMgr.getVirtualFile("/test-header.h",
267                                                  HeaderBuf->getBufferSize(), 0);
268   SourceMgr.overrideFileContents(headerFile, std::move(HeaderBuf));
269 
270   TrivialModuleLoader ModLoader;
271   HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
272                           Diags, LangOpts, &*Target);
273   Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
274                   SourceMgr, HeaderInfo, ModLoader,
275                   /*IILookup =*/nullptr,
276                   /*OwnsHeaderSearch =*/false);
277   PP.Initialize(*Target);
278   PP.EnterMainSourceFile();
279 
280   std::vector<Token> toks;
281   while (1) {
282     Token tok;
283     PP.Lex(tok);
284     if (tok.is(tok::eof))
285       break;
286     toks.push_back(tok);
287   }
288 
289   // Make sure we got the tokens that we expected.
290   ASSERT_EQ(4U, toks.size());
291   ASSERT_EQ(tok::numeric_constant, toks[0].getKind());
292   ASSERT_EQ(tok::numeric_constant, toks[1].getKind());
293   ASSERT_EQ(tok::numeric_constant, toks[2].getKind());
294   ASSERT_EQ(tok::numeric_constant, toks[3].getKind());
295 
296   SourceLocation defLoc = SourceMgr.translateLineCol(mainFileID, 2, 13);
297   SourceLocation loc1 = SourceMgr.translateLineCol(mainFileID, 3, 8);
298   SourceLocation loc2 = SourceMgr.translateLineCol(mainFileID, 4, 4);
299   SourceLocation loc3 = SourceMgr.translateLineCol(mainFileID, 5, 7);
300   SourceLocation defLoc2 = SourceMgr.translateLineCol(mainFileID, 6, 22);
301   defLoc = SourceMgr.getMacroArgExpandedLocation(defLoc);
302   loc1 = SourceMgr.getMacroArgExpandedLocation(loc1);
303   loc2 = SourceMgr.getMacroArgExpandedLocation(loc2);
304   loc3 = SourceMgr.getMacroArgExpandedLocation(loc3);
305   defLoc2 = SourceMgr.getMacroArgExpandedLocation(defLoc2);
306 
307   EXPECT_TRUE(defLoc.isFileID());
308   EXPECT_TRUE(loc1.isFileID());
309   EXPECT_TRUE(SourceMgr.isMacroArgExpansion(loc2));
310   EXPECT_TRUE(SourceMgr.isMacroArgExpansion(loc3));
311   EXPECT_EQ(loc2, toks[1].getLocation());
312   EXPECT_EQ(loc3, toks[2].getLocation());
313   EXPECT_TRUE(defLoc2.isFileID());
314 }
315 
316 namespace {
317 
318 struct MacroAction {
319   enum Kind { kExpansion, kDefinition, kUnDefinition};
320 
321   SourceLocation Loc;
322   std::string Name;
323   unsigned MAKind : 3;
324 
325   MacroAction(SourceLocation Loc, StringRef Name, unsigned K)
326     : Loc(Loc), Name(Name), MAKind(K) { }
327 
328   bool isExpansion() const { return MAKind == kExpansion; }
329   bool isDefinition() const { return MAKind & kDefinition; }
330   bool isUnDefinition() const { return MAKind & kUnDefinition; }
331 };
332 
333 class MacroTracker : public PPCallbacks {
334   std::vector<MacroAction> &Macros;
335 
336 public:
337   explicit MacroTracker(std::vector<MacroAction> &Macros) : Macros(Macros) { }
338 
339   void MacroDefined(const Token &MacroNameTok,
340                     const MacroDirective *MD) override {
341     Macros.push_back(MacroAction(MD->getLocation(),
342                                  MacroNameTok.getIdentifierInfo()->getName(),
343                                  MacroAction::kDefinition));
344   }
345   void MacroUndefined(const Token &MacroNameTok,
346                       const MacroDefinition &MD,
347                       const MacroDirective  *UD) override {
348     Macros.push_back(
349         MacroAction(UD ? UD->getLocation() : SourceLocation(),
350                     MacroNameTok.getIdentifierInfo()->getName(),
351                     UD ? MacroAction::kDefinition | MacroAction::kUnDefinition
352                        : MacroAction::kUnDefinition));
353   }
354   void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
355                     SourceRange Range, const MacroArgs *Args) override {
356     Macros.push_back(MacroAction(MacroNameTok.getLocation(),
357                                  MacroNameTok.getIdentifierInfo()->getName(),
358                                  MacroAction::kExpansion));
359   }
360 };
361 
362 }
363 
364 TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithMacroInInclude) {
365   const char *header =
366     "#define MACRO_IN_INCLUDE 0\n"
367     "#define MACRO_DEFINED\n"
368     "#undef MACRO_DEFINED\n"
369     "#undef MACRO_UNDEFINED\n";
370 
371   const char *main =
372     "#define M(x) x\n"
373     "#define INC \"/test-header.h\"\n"
374     "#include M(INC)\n"
375     "#define INC2 </test-header.h>\n"
376     "#include M(INC2)\n";
377 
378   std::unique_ptr<llvm::MemoryBuffer> HeaderBuf =
379       llvm::MemoryBuffer::getMemBuffer(header);
380   std::unique_ptr<llvm::MemoryBuffer> MainBuf =
381       llvm::MemoryBuffer::getMemBuffer(main);
382   SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(MainBuf)));
383 
384   const FileEntry *headerFile = FileMgr.getVirtualFile("/test-header.h",
385                                                  HeaderBuf->getBufferSize(), 0);
386   SourceMgr.overrideFileContents(headerFile, std::move(HeaderBuf));
387 
388   TrivialModuleLoader ModLoader;
389   HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
390                           Diags, LangOpts, &*Target);
391   Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
392                   SourceMgr, HeaderInfo, ModLoader,
393                   /*IILookup =*/nullptr,
394                   /*OwnsHeaderSearch =*/false);
395   PP.Initialize(*Target);
396 
397   std::vector<MacroAction> Macros;
398   PP.addPPCallbacks(std::make_unique<MacroTracker>(Macros));
399 
400   PP.EnterMainSourceFile();
401 
402   std::vector<Token> toks;
403   while (1) {
404     Token tok;
405     PP.Lex(tok);
406     if (tok.is(tok::eof))
407       break;
408     toks.push_back(tok);
409   }
410 
411   // Make sure we got the tokens that we expected.
412   ASSERT_EQ(0U, toks.size());
413 
414   ASSERT_EQ(15U, Macros.size());
415   // #define M(x) x
416   ASSERT_TRUE(Macros[0].isDefinition());
417   ASSERT_EQ("M", Macros[0].Name);
418   // #define INC "/test-header.h"
419   ASSERT_TRUE(Macros[1].isDefinition());
420   ASSERT_EQ("INC", Macros[1].Name);
421   // M expansion in #include M(INC)
422   ASSERT_FALSE(Macros[2].isDefinition());
423   ASSERT_EQ("M", Macros[2].Name);
424   // INC expansion in #include M(INC)
425   ASSERT_TRUE(Macros[3].isExpansion());
426   ASSERT_EQ("INC", Macros[3].Name);
427   // #define MACRO_IN_INCLUDE 0
428   ASSERT_TRUE(Macros[4].isDefinition());
429   ASSERT_EQ("MACRO_IN_INCLUDE", Macros[4].Name);
430   // #define MACRO_DEFINED
431   ASSERT_TRUE(Macros[5].isDefinition());
432   ASSERT_FALSE(Macros[5].isUnDefinition());
433   ASSERT_EQ("MACRO_DEFINED", Macros[5].Name);
434   // #undef MACRO_DEFINED
435   ASSERT_TRUE(Macros[6].isDefinition());
436   ASSERT_TRUE(Macros[6].isUnDefinition());
437   ASSERT_EQ("MACRO_DEFINED", Macros[6].Name);
438   // #undef MACRO_UNDEFINED
439   ASSERT_FALSE(Macros[7].isDefinition());
440   ASSERT_TRUE(Macros[7].isUnDefinition());
441   ASSERT_EQ("MACRO_UNDEFINED", Macros[7].Name);
442   // #define INC2 </test-header.h>
443   ASSERT_TRUE(Macros[8].isDefinition());
444   ASSERT_EQ("INC2", Macros[8].Name);
445   // M expansion in #include M(INC2)
446   ASSERT_FALSE(Macros[9].isDefinition());
447   ASSERT_EQ("M", Macros[9].Name);
448   // INC2 expansion in #include M(INC2)
449   ASSERT_TRUE(Macros[10].isExpansion());
450   ASSERT_EQ("INC2", Macros[10].Name);
451   // #define MACRO_IN_INCLUDE 0
452   ASSERT_TRUE(Macros[11].isDefinition());
453   ASSERT_EQ("MACRO_IN_INCLUDE", Macros[11].Name);
454 
455   // The INC expansion in #include M(INC) comes before the first
456   // MACRO_IN_INCLUDE definition of the included file.
457   EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(Macros[3].Loc, Macros[4].Loc));
458 
459   // The INC2 expansion in #include M(INC2) comes before the second
460   // MACRO_IN_INCLUDE definition of the included file.
461   EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(Macros[10].Loc, Macros[11].Loc));
462 }
463 
464 #endif
465 
466 } // anonymous namespace
467