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