[clang] Adopt new FileManager error-returning APIs
[lldb.git] / clang / unittests / Lex / PPCallbacksTest.cpp
1 //===- unittests/Lex/PPCallbacksTest.cpp - PPCallbacks 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/Lex/Preprocessor.h"
10 #include "clang/AST/ASTConsumer.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/Basic/Diagnostic.h"
13 #include "clang/Basic/DiagnosticOptions.h"
14 #include "clang/Basic/FileManager.h"
15 #include "clang/Basic/LangOptions.h"
16 #include "clang/Basic/SourceManager.h"
17 #include "clang/Basic/TargetInfo.h"
18 #include "clang/Basic/TargetOptions.h"
19 #include "clang/Lex/HeaderSearch.h"
20 #include "clang/Lex/HeaderSearchOptions.h"
21 #include "clang/Lex/ModuleLoader.h"
22 #include "clang/Lex/PreprocessorOptions.h"
23 #include "clang/Parse/Parser.h"
24 #include "clang/Sema/Sema.h"
25 #include "llvm/ADT/SmallString.h"
26 #include "llvm/Support/Path.h"
27 #include "gtest/gtest.h"
28
29 using namespace clang;
30
31 namespace {
32
33 // Stub to collect data from InclusionDirective callbacks.
34 class InclusionDirectiveCallbacks : public PPCallbacks {
35 public:
36   void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
37                           StringRef FileName, bool IsAngled,
38                           CharSourceRange FilenameRange, const FileEntry *File,
39                           StringRef SearchPath, StringRef RelativePath,
40                           const Module *Imported,
41                           SrcMgr::CharacteristicKind FileType) override {
42     this->HashLoc = HashLoc;
43     this->IncludeTok = IncludeTok;
44     this->FileName = FileName.str();
45     this->IsAngled = IsAngled;
46     this->FilenameRange = FilenameRange;
47     this->File = File;
48     this->SearchPath = SearchPath.str();
49     this->RelativePath = RelativePath.str();
50     this->Imported = Imported;
51     this->FileType = FileType;
52   }
53
54   SourceLocation HashLoc;
55   Token IncludeTok;
56   SmallString<16> FileName;
57   bool IsAngled;
58   CharSourceRange FilenameRange;
59   const FileEntry* File;
60   SmallString<16> SearchPath;
61   SmallString<16> RelativePath;
62   const Module* Imported;
63   SrcMgr::CharacteristicKind FileType;
64 };
65
66 class CondDirectiveCallbacks : public PPCallbacks {
67 public:
68   struct Result {
69     SourceRange ConditionRange;
70     ConditionValueKind ConditionValue;
71
72     Result(SourceRange R, ConditionValueKind K)
73         : ConditionRange(R), ConditionValue(K) {}
74   };
75
76   std::vector<Result> Results;
77
78   void If(SourceLocation Loc, SourceRange ConditionRange,
79           ConditionValueKind ConditionValue) override {
80     Results.emplace_back(ConditionRange, ConditionValue);
81   }
82
83   void Elif(SourceLocation Loc, SourceRange ConditionRange,
84             ConditionValueKind ConditionValue, SourceLocation IfLoc) override {
85     Results.emplace_back(ConditionRange, ConditionValue);
86   }
87 };
88
89 // Stub to collect data from PragmaOpenCLExtension callbacks.
90 class PragmaOpenCLExtensionCallbacks : public PPCallbacks {
91 public:
92   typedef struct {
93     SmallString<16> Name;
94     unsigned State;
95   } CallbackParameters;
96
97   PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {}
98
99   void PragmaOpenCLExtension(clang::SourceLocation NameLoc,
100                              const clang::IdentifierInfo *Name,
101                              clang::SourceLocation StateLoc,
102                              unsigned State) override {
103       this->NameLoc = NameLoc;
104       this->Name = Name->getName();
105       this->StateLoc = StateLoc;
106       this->State = State;
107   }
108
109   SourceLocation NameLoc;
110   SmallString<16> Name;
111   SourceLocation StateLoc;
112   unsigned State;
113 };
114
115 // PPCallbacks test fixture.
116 class PPCallbacksTest : public ::testing::Test {
117 protected:
118   PPCallbacksTest()
119       : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
120         FileMgr(FileSystemOptions(), InMemoryFileSystem),
121         DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()),
122         Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()),
123         SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) {
124     TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
125     Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
126   }
127
128   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
129   FileManager FileMgr;
130   IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
131   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
132   DiagnosticsEngine Diags;
133   SourceManager SourceMgr;
134   LangOptions LangOpts;
135   std::shared_ptr<TargetOptions> TargetOpts;
136   IntrusiveRefCntPtr<TargetInfo> Target;
137
138   // Register a header path as a known file and add its location
139   // to search path.
140   void AddFakeHeader(HeaderSearch &HeaderInfo, const char *HeaderPath,
141                      bool IsSystemHeader) {
142     // Tell FileMgr about header.
143     InMemoryFileSystem->addFile(HeaderPath, 0,
144                                 llvm::MemoryBuffer::getMemBuffer("\n"));
145
146     // Add header's parent path to search path.
147     StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath);
148     auto DE = FileMgr.getDirectory(SearchPath);
149     DirectoryLookup DL(*DE, SrcMgr::C_User, false);
150     HeaderInfo.AddSearchPath(DL, IsSystemHeader);
151   }
152
153   // Get the raw source string of the range.
154   StringRef GetSourceString(CharSourceRange Range) {
155     const char* B = SourceMgr.getCharacterData(Range.getBegin());
156     const char* E = SourceMgr.getCharacterData(Range.getEnd());
157
158     return StringRef(B, E - B);
159   }
160
161   StringRef GetSourceStringToEnd(CharSourceRange Range) {
162     const char *B = SourceMgr.getCharacterData(Range.getBegin());
163     const char *E = SourceMgr.getCharacterData(Range.getEnd());
164
165     return StringRef(
166         B,
167         E - B + Lexer::MeasureTokenLength(Range.getEnd(), SourceMgr, LangOpts));
168   }
169
170   // Run lexer over SourceText and collect FilenameRange from
171   // the InclusionDirective callback.
172   CharSourceRange InclusionDirectiveFilenameRange(const char *SourceText,
173                                                   const char *HeaderPath,
174                                                   bool SystemHeader) {
175     std::unique_ptr<llvm::MemoryBuffer> Buf =
176         llvm::MemoryBuffer::getMemBuffer(SourceText);
177     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
178
179     TrivialModuleLoader ModLoader;
180
181     HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
182                             Diags, LangOpts, Target.get());
183     AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
184
185     Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
186                     SourceMgr, HeaderInfo, ModLoader,
187                     /*IILookup =*/nullptr,
188                     /*OwnsHeaderSearch =*/false);
189     return InclusionDirectiveCallback(PP)->FilenameRange;
190   }
191
192   SrcMgr::CharacteristicKind InclusionDirectiveCharacteristicKind(
193       const char *SourceText, const char *HeaderPath, bool SystemHeader) {
194     std::unique_ptr<llvm::MemoryBuffer> Buf =
195         llvm::MemoryBuffer::getMemBuffer(SourceText);
196     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
197
198     TrivialModuleLoader ModLoader;
199
200     HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
201                             Diags, LangOpts, Target.get());
202     AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
203
204     Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
205                     SourceMgr, HeaderInfo, ModLoader,
206                     /*IILookup =*/nullptr,
207                     /*OwnsHeaderSearch =*/false);
208     return InclusionDirectiveCallback(PP)->FileType;
209   }
210
211   InclusionDirectiveCallbacks *InclusionDirectiveCallback(Preprocessor &PP) {
212     PP.Initialize(*Target);
213     InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks;
214     PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
215
216     // Lex source text.
217     PP.EnterMainSourceFile();
218
219     while (true) {
220       Token Tok;
221       PP.Lex(Tok);
222       if (Tok.is(tok::eof))
223         break;
224     }
225
226     // Callbacks have been executed at this point -- return filename range.
227     return Callbacks;
228   }
229
230   std::vector<CondDirectiveCallbacks::Result>
231   DirectiveExprRange(StringRef SourceText) {
232     TrivialModuleLoader ModLoader;
233     std::unique_ptr<llvm::MemoryBuffer> Buf =
234         llvm::MemoryBuffer::getMemBuffer(SourceText);
235     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
236     HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
237                             Diags, LangOpts, Target.get());
238     Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
239                     SourceMgr, HeaderInfo, ModLoader,
240                     /*IILookup =*/nullptr,
241                     /*OwnsHeaderSearch =*/false);
242     PP.Initialize(*Target);
243     auto *Callbacks = new CondDirectiveCallbacks;
244     PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
245
246     // Lex source text.
247     PP.EnterMainSourceFile();
248
249     while (true) {
250       Token Tok;
251       PP.Lex(Tok);
252       if (Tok.is(tok::eof))
253         break;
254     }
255
256     return Callbacks->Results;
257   }
258
259   PragmaOpenCLExtensionCallbacks::CallbackParameters
260   PragmaOpenCLExtensionCall(const char *SourceText) {
261     LangOptions OpenCLLangOpts;
262     OpenCLLangOpts.OpenCL = 1;
263
264     std::unique_ptr<llvm::MemoryBuffer> SourceBuf =
265         llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl");
266     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
267
268     TrivialModuleLoader ModLoader;
269     HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
270                             Diags, OpenCLLangOpts, Target.get());
271
272     Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags,
273                     OpenCLLangOpts, SourceMgr, HeaderInfo, ModLoader,
274                     /*IILookup =*/nullptr,
275                     /*OwnsHeaderSearch =*/false);
276     PP.Initialize(*Target);
277
278     // parser actually sets correct pragma handlers for preprocessor
279     // according to LangOptions, so we init Parser to register opencl
280     // pragma handlers
281     ASTContext Context(OpenCLLangOpts, SourceMgr, PP.getIdentifierTable(),
282                        PP.getSelectorTable(), PP.getBuiltinInfo());
283     Context.InitBuiltinTypes(*Target);
284
285     ASTConsumer Consumer;
286     Sema S(PP, Context, Consumer);
287     Parser P(PP, S, false);
288     PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks;
289     PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
290
291     // Lex source text.
292     PP.EnterMainSourceFile();
293     while (true) {
294       Token Tok;
295       PP.Lex(Tok);
296       if (Tok.is(tok::eof))
297         break;
298     }
299
300     PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = {
301       Callbacks->Name,
302       Callbacks->State
303     };
304     return RetVal;
305   }
306 };
307
308 TEST_F(PPCallbacksTest, UserFileCharacteristics) {
309   const char *Source = "#include \"quoted.h\"\n";
310
311   SrcMgr::CharacteristicKind Kind =
312       InclusionDirectiveCharacteristicKind(Source, "/quoted.h", false);
313
314   ASSERT_EQ(SrcMgr::CharacteristicKind::C_User, Kind);
315 }
316
317 TEST_F(PPCallbacksTest, QuotedFilename) {
318   const char* Source =
319     "#include \"quoted.h\"\n";
320
321   CharSourceRange Range =
322     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
323
324   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
325 }
326
327 TEST_F(PPCallbacksTest, AngledFilename) {
328   const char* Source =
329     "#include <angled.h>\n";
330
331   CharSourceRange Range =
332     InclusionDirectiveFilenameRange(Source, "/angled.h", true);
333
334   ASSERT_EQ("<angled.h>", GetSourceString(Range));
335 }
336
337 TEST_F(PPCallbacksTest, QuotedInMacro) {
338   const char* Source =
339     "#define MACRO_QUOTED \"quoted.h\"\n"
340     "#include MACRO_QUOTED\n";
341
342   CharSourceRange Range =
343     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
344
345   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
346 }
347
348 TEST_F(PPCallbacksTest, AngledInMacro) {
349   const char* Source =
350     "#define MACRO_ANGLED <angled.h>\n"
351     "#include MACRO_ANGLED\n";
352
353   CharSourceRange Range =
354     InclusionDirectiveFilenameRange(Source, "/angled.h", true);
355
356   ASSERT_EQ("<angled.h>", GetSourceString(Range));
357 }
358
359 TEST_F(PPCallbacksTest, StringizedMacroArgument) {
360   const char* Source =
361     "#define MACRO_STRINGIZED(x) #x\n"
362     "#include MACRO_STRINGIZED(quoted.h)\n";
363
364   CharSourceRange Range =
365     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
366
367   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
368 }
369
370 TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) {
371   const char* Source =
372     "#define MACRO_ANGLED <angled.h>\n"
373     "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
374     "#include MACRO_CONCAT(MACRO, ANGLED)\n";
375
376   CharSourceRange Range =
377     InclusionDirectiveFilenameRange(Source, "/angled.h", false);
378
379   ASSERT_EQ("<angled.h>", GetSourceString(Range));
380 }
381
382 TEST_F(PPCallbacksTest, TrigraphFilename) {
383   const char* Source =
384     "#include \"tri\?\?-graph.h\"\n";
385
386   CharSourceRange Range =
387     InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
388
389   ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
390 }
391
392 TEST_F(PPCallbacksTest, TrigraphInMacro) {
393   const char* Source =
394     "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
395     "#include MACRO_TRIGRAPH\n";
396
397   CharSourceRange Range =
398     InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
399
400   ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
401 }
402
403 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) {
404   const char* Source =
405     "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n";
406
407   PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
408     PragmaOpenCLExtensionCall(Source);
409
410   ASSERT_EQ("cl_khr_fp64", Parameters.Name);
411   unsigned ExpectedState = 1;
412   ASSERT_EQ(ExpectedState, Parameters.State);
413 }
414
415 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) {
416   const char* Source =
417     "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n";
418
419   PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
420     PragmaOpenCLExtensionCall(Source);
421
422   ASSERT_EQ("cl_khr_fp16", Parameters.Name);
423   unsigned ExpectedState = 0;
424   ASSERT_EQ(ExpectedState, Parameters.State);
425 }
426
427 TEST_F(PPCallbacksTest, DirectiveExprRanges) {
428   const auto &Results1 = DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n");
429   EXPECT_EQ(Results1.size(), 1U);
430   EXPECT_EQ(
431       GetSourceStringToEnd(CharSourceRange(Results1[0].ConditionRange, false)),
432       "FLUZZY_FLOOF");
433
434   const auto &Results2 = DirectiveExprRange("#if 1 + 4 < 7\n#endif\n");
435   EXPECT_EQ(Results2.size(), 1U);
436   EXPECT_EQ(
437       GetSourceStringToEnd(CharSourceRange(Results2[0].ConditionRange, false)),
438       "1 + 4 < 7");
439
440   const auto &Results3 = DirectiveExprRange("#if 1 + \\\n  2\n#endif\n");
441   EXPECT_EQ(Results3.size(), 1U);
442   EXPECT_EQ(
443       GetSourceStringToEnd(CharSourceRange(Results3[0].ConditionRange, false)),
444       "1 + \\\n  2");
445
446   const auto &Results4 = DirectiveExprRange("#if 0\n#elif FLOOFY\n#endif\n");
447   EXPECT_EQ(Results4.size(), 2U);
448   EXPECT_EQ(
449       GetSourceStringToEnd(CharSourceRange(Results4[0].ConditionRange, false)),
450       "0");
451   EXPECT_EQ(
452       GetSourceStringToEnd(CharSourceRange(Results4[1].ConditionRange, false)),
453       "FLOOFY");
454
455   const auto &Results5 = DirectiveExprRange("#if 1\n#elif FLOOFY\n#endif\n");
456   EXPECT_EQ(Results5.size(), 2U);
457   EXPECT_EQ(
458       GetSourceStringToEnd(CharSourceRange(Results5[0].ConditionRange, false)),
459       "1");
460   EXPECT_EQ(
461       GetSourceStringToEnd(CharSourceRange(Results5[1].ConditionRange, false)),
462       "FLOOFY");
463
464   const auto &Results6 =
465       DirectiveExprRange("#if defined(FLUZZY_FLOOF)\n#endif\n");
466   EXPECT_EQ(Results6.size(), 1U);
467   EXPECT_EQ(
468       GetSourceStringToEnd(CharSourceRange(Results6[0].ConditionRange, false)),
469       "defined(FLUZZY_FLOOF)");
470
471   const auto &Results7 =
472       DirectiveExprRange("#if 1\n#elif defined(FLOOFY)\n#endif\n");
473   EXPECT_EQ(Results7.size(), 2U);
474   EXPECT_EQ(
475       GetSourceStringToEnd(CharSourceRange(Results7[0].ConditionRange, false)),
476       "1");
477   EXPECT_EQ(
478       GetSourceStringToEnd(CharSourceRange(Results7[1].ConditionRange, false)),
479       "defined(FLOOFY)");
480
481   const auto &Results8 =
482       DirectiveExprRange("#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n");
483   EXPECT_EQ(Results8.size(), 1U);
484   EXPECT_EQ(
485       GetSourceStringToEnd(CharSourceRange(Results8[0].ConditionRange, false)),
486       "__FILE__ > FLOOFY");
487   EXPECT_EQ(
488       Lexer::getSourceText(CharSourceRange(Results8[0].ConditionRange, false),
489                            SourceMgr, LangOpts),
490       "__FILE__ > FLOOFY");
491 }
492
493 } // namespace