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