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:
InclusionDirective(SourceLocation HashLoc,const Token & IncludeTok,StringRef FileName,bool IsAngled,CharSourceRange FilenameRange,Optional<FileEntryRef> File,StringRef SearchPath,StringRef RelativePath,const Module * Imported,SrcMgr::CharacteristicKind FileType)36   void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
37                           StringRef FileName, bool IsAngled,
38                           CharSourceRange FilenameRange,
39                           Optional<FileEntryRef> File, StringRef SearchPath,
40                           StringRef RelativePath, 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   Optional<FileEntryRef> 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 
Result__anonec3cc9fe0111::CondDirectiveCallbacks::Result72     Result(SourceRange R, ConditionValueKind K)
73         : ConditionRange(R), ConditionValue(K) {}
74   };
75 
76   std::vector<Result> Results;
77 
If(SourceLocation Loc,SourceRange ConditionRange,ConditionValueKind ConditionValue)78   void If(SourceLocation Loc, SourceRange ConditionRange,
79           ConditionValueKind ConditionValue) override {
80     Results.emplace_back(ConditionRange, ConditionValue);
81   }
82 
Elif(SourceLocation Loc,SourceRange ConditionRange,ConditionValueKind ConditionValue,SourceLocation IfLoc)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 
PragmaOpenCLExtensionCallbacks()97   PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {}
98 
PragmaOpenCLExtension(clang::SourceLocation NameLoc,const clang::IdentifierInfo * Name,clang::SourceLocation StateLoc,unsigned State)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 class PragmaMarkCallbacks : public PPCallbacks {
116 public:
117   struct Mark {
118     SourceLocation Location;
119     std::string Trivia;
120   };
121 
122   std::vector<Mark> Marks;
123 
PragmaMark(SourceLocation Loc,StringRef Trivia)124   void PragmaMark(SourceLocation Loc, StringRef Trivia) override {
125     Marks.emplace_back(Mark{Loc, Trivia.str()});
126   }
127 };
128 
129 // PPCallbacks test fixture.
130 class PPCallbacksTest : public ::testing::Test {
131 protected:
PPCallbacksTest()132   PPCallbacksTest()
133       : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
134         FileMgr(FileSystemOptions(), InMemoryFileSystem),
135         DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()),
136         Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()),
137         SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) {
138     TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
139     Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
140   }
141 
142   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
143   FileManager FileMgr;
144   IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
145   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
146   DiagnosticsEngine Diags;
147   SourceManager SourceMgr;
148   LangOptions LangOpts;
149   std::shared_ptr<TargetOptions> TargetOpts;
150   IntrusiveRefCntPtr<TargetInfo> Target;
151 
152   // Register a header path as a known file and add its location
153   // to search path.
AddFakeHeader(HeaderSearch & HeaderInfo,const char * HeaderPath,bool IsSystemHeader)154   void AddFakeHeader(HeaderSearch &HeaderInfo, const char *HeaderPath,
155                      bool IsSystemHeader) {
156     // Tell FileMgr about header.
157     InMemoryFileSystem->addFile(HeaderPath, 0,
158                                 llvm::MemoryBuffer::getMemBuffer("\n"));
159 
160     // Add header's parent path to search path.
161     StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath);
162     auto DE = FileMgr.getOptionalDirectoryRef(SearchPath);
163     DirectoryLookup DL(*DE, SrcMgr::C_User, false);
164     HeaderInfo.AddSearchPath(DL, IsSystemHeader);
165   }
166 
167   // Get the raw source string of the range.
GetSourceString(CharSourceRange Range)168   StringRef GetSourceString(CharSourceRange Range) {
169     const char* B = SourceMgr.getCharacterData(Range.getBegin());
170     const char* E = SourceMgr.getCharacterData(Range.getEnd());
171 
172     return StringRef(B, E - B);
173   }
174 
GetSourceStringToEnd(CharSourceRange Range)175   StringRef GetSourceStringToEnd(CharSourceRange Range) {
176     const char *B = SourceMgr.getCharacterData(Range.getBegin());
177     const char *E = SourceMgr.getCharacterData(Range.getEnd());
178 
179     return StringRef(
180         B,
181         E - B + Lexer::MeasureTokenLength(Range.getEnd(), SourceMgr, LangOpts));
182   }
183 
184   // Run lexer over SourceText and collect FilenameRange from
185   // the InclusionDirective callback.
InclusionDirectiveFilenameRange(const char * SourceText,const char * HeaderPath,bool SystemHeader)186   CharSourceRange InclusionDirectiveFilenameRange(const char *SourceText,
187                                                   const char *HeaderPath,
188                                                   bool SystemHeader) {
189     std::unique_ptr<llvm::MemoryBuffer> Buf =
190         llvm::MemoryBuffer::getMemBuffer(SourceText);
191     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
192 
193     TrivialModuleLoader ModLoader;
194 
195     HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
196                             Diags, LangOpts, Target.get());
197     AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
198 
199     Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
200                     SourceMgr, HeaderInfo, ModLoader,
201                     /*IILookup =*/nullptr,
202                     /*OwnsHeaderSearch =*/false);
203     return InclusionDirectiveCallback(PP)->FilenameRange;
204   }
205 
InclusionDirectiveCharacteristicKind(const char * SourceText,const char * HeaderPath,bool SystemHeader)206   SrcMgr::CharacteristicKind InclusionDirectiveCharacteristicKind(
207       const char *SourceText, const char *HeaderPath, bool SystemHeader) {
208     std::unique_ptr<llvm::MemoryBuffer> Buf =
209         llvm::MemoryBuffer::getMemBuffer(SourceText);
210     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
211 
212     TrivialModuleLoader ModLoader;
213 
214     HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
215                             Diags, LangOpts, Target.get());
216     AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
217 
218     Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
219                     SourceMgr, HeaderInfo, ModLoader,
220                     /*IILookup =*/nullptr,
221                     /*OwnsHeaderSearch =*/false);
222     return InclusionDirectiveCallback(PP)->FileType;
223   }
224 
InclusionDirectiveCallback(Preprocessor & PP)225   InclusionDirectiveCallbacks *InclusionDirectiveCallback(Preprocessor &PP) {
226     PP.Initialize(*Target);
227     InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks;
228     PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
229 
230     // Lex source text.
231     PP.EnterMainSourceFile();
232 
233     while (true) {
234       Token Tok;
235       PP.Lex(Tok);
236       if (Tok.is(tok::eof))
237         break;
238     }
239 
240     // Callbacks have been executed at this point -- return filename range.
241     return Callbacks;
242   }
243 
244   std::vector<CondDirectiveCallbacks::Result>
DirectiveExprRange(StringRef SourceText)245   DirectiveExprRange(StringRef SourceText) {
246     TrivialModuleLoader ModLoader;
247     std::unique_ptr<llvm::MemoryBuffer> Buf =
248         llvm::MemoryBuffer::getMemBuffer(SourceText);
249     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
250     HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
251                             Diags, LangOpts, Target.get());
252     Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
253                     SourceMgr, HeaderInfo, ModLoader,
254                     /*IILookup =*/nullptr,
255                     /*OwnsHeaderSearch =*/false);
256     PP.Initialize(*Target);
257     auto *Callbacks = new CondDirectiveCallbacks;
258     PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
259 
260     // Lex source text.
261     PP.EnterMainSourceFile();
262 
263     while (true) {
264       Token Tok;
265       PP.Lex(Tok);
266       if (Tok.is(tok::eof))
267         break;
268     }
269 
270     return Callbacks->Results;
271   }
272 
273   std::vector<PragmaMarkCallbacks::Mark>
PragmaMarkCall(const char * SourceText)274   PragmaMarkCall(const char *SourceText) {
275     std::unique_ptr<llvm::MemoryBuffer> SourceBuf =
276         llvm::MemoryBuffer::getMemBuffer(SourceText, "test.c");
277     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
278 
279     HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
280                             Diags, LangOpts, Target.get());
281     TrivialModuleLoader ModLoader;
282 
283     Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
284                     SourceMgr, HeaderInfo, ModLoader, /*IILookup=*/nullptr,
285                     /*OwnsHeaderSearch=*/false);
286     PP.Initialize(*Target);
287 
288     auto *Callbacks = new PragmaMarkCallbacks;
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     return Callbacks->Marks;
301   }
302 
303   PragmaOpenCLExtensionCallbacks::CallbackParameters
PragmaOpenCLExtensionCall(const char * SourceText)304   PragmaOpenCLExtensionCall(const char *SourceText) {
305     LangOptions OpenCLLangOpts;
306     OpenCLLangOpts.OpenCL = 1;
307 
308     std::unique_ptr<llvm::MemoryBuffer> SourceBuf =
309         llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl");
310     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
311 
312     TrivialModuleLoader ModLoader;
313     HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
314                             Diags, OpenCLLangOpts, Target.get());
315 
316     Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags,
317                     OpenCLLangOpts, SourceMgr, HeaderInfo, ModLoader,
318                     /*IILookup =*/nullptr,
319                     /*OwnsHeaderSearch =*/false);
320     PP.Initialize(*Target);
321 
322     // parser actually sets correct pragma handlers for preprocessor
323     // according to LangOptions, so we init Parser to register opencl
324     // pragma handlers
325     ASTContext Context(OpenCLLangOpts, SourceMgr, PP.getIdentifierTable(),
326                        PP.getSelectorTable(), PP.getBuiltinInfo(), PP.TUKind);
327     Context.InitBuiltinTypes(*Target);
328 
329     ASTConsumer Consumer;
330     Sema S(PP, Context, Consumer);
331     Parser P(PP, S, false);
332     PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks;
333     PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
334 
335     // Lex source text.
336     PP.EnterMainSourceFile();
337     while (true) {
338       Token Tok;
339       PP.Lex(Tok);
340       if (Tok.is(tok::eof))
341         break;
342     }
343 
344     PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = {
345       Callbacks->Name,
346       Callbacks->State
347     };
348     return RetVal;
349   }
350 };
351 
TEST_F(PPCallbacksTest,UserFileCharacteristics)352 TEST_F(PPCallbacksTest, UserFileCharacteristics) {
353   const char *Source = "#include \"quoted.h\"\n";
354 
355   SrcMgr::CharacteristicKind Kind =
356       InclusionDirectiveCharacteristicKind(Source, "/quoted.h", false);
357 
358   ASSERT_EQ(SrcMgr::CharacteristicKind::C_User, Kind);
359 }
360 
TEST_F(PPCallbacksTest,QuotedFilename)361 TEST_F(PPCallbacksTest, QuotedFilename) {
362   const char* Source =
363     "#include \"quoted.h\"\n";
364 
365   CharSourceRange Range =
366     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
367 
368   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
369 }
370 
TEST_F(PPCallbacksTest,AngledFilename)371 TEST_F(PPCallbacksTest, AngledFilename) {
372   const char* Source =
373     "#include <angled.h>\n";
374 
375   CharSourceRange Range =
376     InclusionDirectiveFilenameRange(Source, "/angled.h", true);
377 
378   ASSERT_EQ("<angled.h>", GetSourceString(Range));
379 }
380 
TEST_F(PPCallbacksTest,QuotedInMacro)381 TEST_F(PPCallbacksTest, QuotedInMacro) {
382   const char* Source =
383     "#define MACRO_QUOTED \"quoted.h\"\n"
384     "#include MACRO_QUOTED\n";
385 
386   CharSourceRange Range =
387     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
388 
389   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
390 }
391 
TEST_F(PPCallbacksTest,AngledInMacro)392 TEST_F(PPCallbacksTest, AngledInMacro) {
393   const char* Source =
394     "#define MACRO_ANGLED <angled.h>\n"
395     "#include MACRO_ANGLED\n";
396 
397   CharSourceRange Range =
398     InclusionDirectiveFilenameRange(Source, "/angled.h", true);
399 
400   ASSERT_EQ("<angled.h>", GetSourceString(Range));
401 }
402 
TEST_F(PPCallbacksTest,StringizedMacroArgument)403 TEST_F(PPCallbacksTest, StringizedMacroArgument) {
404   const char* Source =
405     "#define MACRO_STRINGIZED(x) #x\n"
406     "#include MACRO_STRINGIZED(quoted.h)\n";
407 
408   CharSourceRange Range =
409     InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
410 
411   ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
412 }
413 
TEST_F(PPCallbacksTest,ConcatenatedMacroArgument)414 TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) {
415   const char* Source =
416     "#define MACRO_ANGLED <angled.h>\n"
417     "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
418     "#include MACRO_CONCAT(MACRO, ANGLED)\n";
419 
420   CharSourceRange Range =
421     InclusionDirectiveFilenameRange(Source, "/angled.h", false);
422 
423   ASSERT_EQ("<angled.h>", GetSourceString(Range));
424 }
425 
TEST_F(PPCallbacksTest,TrigraphFilename)426 TEST_F(PPCallbacksTest, TrigraphFilename) {
427   const char* Source =
428     "#include \"tri\?\?-graph.h\"\n";
429 
430   CharSourceRange Range =
431     InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
432 
433   ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
434 }
435 
TEST_F(PPCallbacksTest,TrigraphInMacro)436 TEST_F(PPCallbacksTest, TrigraphInMacro) {
437   const char* Source =
438     "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
439     "#include MACRO_TRIGRAPH\n";
440 
441   CharSourceRange Range =
442     InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
443 
444   ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
445 }
446 
TEST_F(PPCallbacksTest,OpenCLExtensionPragmaEnabled)447 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) {
448   const char* Source =
449     "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n";
450 
451   PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
452     PragmaOpenCLExtensionCall(Source);
453 
454   ASSERT_EQ("cl_khr_fp64", Parameters.Name);
455   unsigned ExpectedState = 1;
456   ASSERT_EQ(ExpectedState, Parameters.State);
457 }
458 
TEST_F(PPCallbacksTest,OpenCLExtensionPragmaDisabled)459 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) {
460   const char* Source =
461     "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n";
462 
463   PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
464     PragmaOpenCLExtensionCall(Source);
465 
466   ASSERT_EQ("cl_khr_fp16", Parameters.Name);
467   unsigned ExpectedState = 0;
468   ASSERT_EQ(ExpectedState, Parameters.State);
469 }
470 
TEST_F(PPCallbacksTest,CollectMarks)471 TEST_F(PPCallbacksTest, CollectMarks) {
472   const char *Source =
473     "#pragma mark\n"
474     "#pragma mark\r\n"
475     "#pragma mark - trivia\n"
476     "#pragma mark - trivia\r\n";
477 
478   auto Marks = PragmaMarkCall(Source);
479 
480   ASSERT_EQ(4u, Marks.size());
481   ASSERT_TRUE(Marks[0].Trivia.empty());
482   ASSERT_TRUE(Marks[1].Trivia.empty());
483   ASSERT_FALSE(Marks[2].Trivia.empty());
484   ASSERT_FALSE(Marks[3].Trivia.empty());
485   ASSERT_EQ(" - trivia", Marks[2].Trivia);
486   ASSERT_EQ(" - trivia", Marks[3].Trivia);
487 }
488 
TEST_F(PPCallbacksTest,DirectiveExprRanges)489 TEST_F(PPCallbacksTest, DirectiveExprRanges) {
490   const auto &Results1 = DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n");
491   EXPECT_EQ(Results1.size(), 1U);
492   EXPECT_EQ(
493       GetSourceStringToEnd(CharSourceRange(Results1[0].ConditionRange, false)),
494       "FLUZZY_FLOOF");
495 
496   const auto &Results2 = DirectiveExprRange("#if 1 + 4 < 7\n#endif\n");
497   EXPECT_EQ(Results2.size(), 1U);
498   EXPECT_EQ(
499       GetSourceStringToEnd(CharSourceRange(Results2[0].ConditionRange, false)),
500       "1 + 4 < 7");
501 
502   const auto &Results3 = DirectiveExprRange("#if 1 + \\\n  2\n#endif\n");
503   EXPECT_EQ(Results3.size(), 1U);
504   EXPECT_EQ(
505       GetSourceStringToEnd(CharSourceRange(Results3[0].ConditionRange, false)),
506       "1 + \\\n  2");
507 
508   const auto &Results4 = DirectiveExprRange("#if 0\n#elif FLOOFY\n#endif\n");
509   EXPECT_EQ(Results4.size(), 2U);
510   EXPECT_EQ(
511       GetSourceStringToEnd(CharSourceRange(Results4[0].ConditionRange, false)),
512       "0");
513   EXPECT_EQ(
514       GetSourceStringToEnd(CharSourceRange(Results4[1].ConditionRange, false)),
515       "FLOOFY");
516 
517   const auto &Results5 = DirectiveExprRange("#if 1\n#elif FLOOFY\n#endif\n");
518   EXPECT_EQ(Results5.size(), 2U);
519   EXPECT_EQ(
520       GetSourceStringToEnd(CharSourceRange(Results5[0].ConditionRange, false)),
521       "1");
522   EXPECT_EQ(
523       GetSourceStringToEnd(CharSourceRange(Results5[1].ConditionRange, false)),
524       "FLOOFY");
525 
526   const auto &Results6 =
527       DirectiveExprRange("#if defined(FLUZZY_FLOOF)\n#endif\n");
528   EXPECT_EQ(Results6.size(), 1U);
529   EXPECT_EQ(
530       GetSourceStringToEnd(CharSourceRange(Results6[0].ConditionRange, false)),
531       "defined(FLUZZY_FLOOF)");
532 
533   const auto &Results7 =
534       DirectiveExprRange("#if 1\n#elif defined(FLOOFY)\n#endif\n");
535   EXPECT_EQ(Results7.size(), 2U);
536   EXPECT_EQ(
537       GetSourceStringToEnd(CharSourceRange(Results7[0].ConditionRange, false)),
538       "1");
539   EXPECT_EQ(
540       GetSourceStringToEnd(CharSourceRange(Results7[1].ConditionRange, false)),
541       "defined(FLOOFY)");
542 
543   const auto &Results8 =
544       DirectiveExprRange("#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n");
545   EXPECT_EQ(Results8.size(), 1U);
546   EXPECT_EQ(
547       GetSourceStringToEnd(CharSourceRange(Results8[0].ConditionRange, false)),
548       "__FILE__ > FLOOFY");
549   EXPECT_EQ(
550       Lexer::getSourceText(CharSourceRange(Results8[0].ConditionRange, false),
551                            SourceMgr, LangOpts),
552       "__FILE__ > FLOOFY");
553 }
554 
555 } // namespace
556