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()), |
139 | Diags(DiagID, DiagOpts, 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 | 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 AddFakeHeader(HeaderSearch &HeaderInfo, const char *HeaderPath, |
158 | bool IsSystemHeader) { |
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 *HeaderPath, |
191 | bool SystemHeader) { |
192 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
193 | llvm::MemoryBuffer::getMemBuffer(InputData: SourceText); |
194 | SourceMgr.setMainFileID(SourceMgr.createFileID(Buffer: std::move(Buf))); |
195 | |
196 | HeaderSearchOptions HSOpts; |
197 | TrivialModuleLoader ModLoader; |
198 | |
199 | HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get()); |
200 | AddFakeHeader(HeaderInfo, HeaderPath, IsSystemHeader: SystemHeader); |
201 | |
202 | PreprocessorOptions PPOpts; |
203 | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, |
204 | /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); |
205 | return InclusionDirectiveCallback(PP)->FilenameRange; |
206 | } |
207 | |
208 | SrcMgr::CharacteristicKind InclusionDirectiveCharacteristicKind( |
209 | const char *SourceText, const char *HeaderPath, bool SystemHeader) { |
210 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
211 | llvm::MemoryBuffer::getMemBuffer(InputData: SourceText); |
212 | SourceMgr.setMainFileID(SourceMgr.createFileID(Buffer: std::move(Buf))); |
213 | |
214 | HeaderSearchOptions HSOpts; |
215 | TrivialModuleLoader ModLoader; |
216 | |
217 | HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get()); |
218 | AddFakeHeader(HeaderInfo, HeaderPath, IsSystemHeader: SystemHeader); |
219 | |
220 | PreprocessorOptions PPOpts; |
221 | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, |
222 | /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); |
223 | return InclusionDirectiveCallback(PP)->FileType; |
224 | } |
225 | |
226 | InclusionDirectiveCallbacks *InclusionDirectiveCallback(Preprocessor &PP) { |
227 | PP.Initialize(Target: *Target); |
228 | InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks; |
229 | PP.addPPCallbacks(C: std::unique_ptr<PPCallbacks>(Callbacks)); |
230 | |
231 | // Lex source text. |
232 | PP.EnterMainSourceFile(); |
233 | PP.LexTokensUntilEOF(); |
234 | |
235 | // Callbacks have been executed at this point -- return filename range. |
236 | return Callbacks; |
237 | } |
238 | |
239 | std::vector<CondDirectiveCallbacks::Result> |
240 | DirectiveExprRange(StringRef SourceText, PreprocessorOptions PPOpts = {}) { |
241 | HeaderSearchOptions HSOpts; |
242 | TrivialModuleLoader ModLoader; |
243 | std::unique_ptr<llvm::MemoryBuffer> Buf = |
244 | llvm::MemoryBuffer::getMemBuffer(InputData: SourceText); |
245 | SourceMgr.setMainFileID(SourceMgr.createFileID(Buffer: std::move(Buf))); |
246 | HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get()); |
247 | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, |
248 | /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); |
249 | PP.Initialize(Target: *Target); |
250 | auto *Callbacks = new CondDirectiveCallbacks; |
251 | PP.addPPCallbacks(C: std::unique_ptr<PPCallbacks>(Callbacks)); |
252 | |
253 | // Lex source text. |
254 | PP.EnterMainSourceFile(); |
255 | PP.LexTokensUntilEOF(); |
256 | |
257 | return Callbacks->Results; |
258 | } |
259 | |
260 | std::vector<PragmaMarkCallbacks::Mark> |
261 | PragmaMarkCall(const char *SourceText) { |
262 | std::unique_ptr<llvm::MemoryBuffer> SourceBuf = |
263 | llvm::MemoryBuffer::getMemBuffer(InputData: SourceText, BufferName: "test.c"); |
264 | SourceMgr.setMainFileID(SourceMgr.createFileID(Buffer: std::move(SourceBuf))); |
265 | |
266 | HeaderSearchOptions HSOpts; |
267 | TrivialModuleLoader ModLoader; |
268 | PreprocessorOptions PPOpts; |
269 | |
270 | HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get()); |
271 | |
272 | Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, |
273 | /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); |
274 | PP.Initialize(Target: *Target); |
275 | |
276 | auto *Callbacks = new PragmaMarkCallbacks; |
277 | PP.addPPCallbacks(C: std::unique_ptr<PPCallbacks>(Callbacks)); |
278 | |
279 | // Lex source text. |
280 | PP.EnterMainSourceFile(); |
281 | PP.LexTokensUntilEOF(); |
282 | |
283 | return Callbacks->Marks; |
284 | } |
285 | |
286 | PragmaOpenCLExtensionCallbacks::CallbackParameters |
287 | PragmaOpenCLExtensionCall(const char *SourceText) { |
288 | LangOptions OpenCLLangOpts; |
289 | OpenCLLangOpts.OpenCL = 1; |
290 | |
291 | std::unique_ptr<llvm::MemoryBuffer> SourceBuf = |
292 | llvm::MemoryBuffer::getMemBuffer(InputData: SourceText, BufferName: "test.cl"); |
293 | SourceMgr.setMainFileID(SourceMgr.createFileID(Buffer: std::move(SourceBuf))); |
294 | |
295 | HeaderSearchOptions HSOpts; |
296 | TrivialModuleLoader ModLoader; |
297 | PreprocessorOptions PPOpts; |
298 | |
299 | HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, OpenCLLangOpts, |
300 | Target.get()); |
301 | |
302 | Preprocessor PP(PPOpts, Diags, OpenCLLangOpts, SourceMgr, HeaderInfo, |
303 | ModLoader, /*IILookup=*/nullptr, |
304 | /*OwnsHeaderSearch=*/false); |
305 | PP.Initialize(Target: *Target); |
306 | |
307 | // parser actually sets correct pragma handlers for preprocessor |
308 | // according to LangOptions, so we init Parser to register opencl |
309 | // pragma handlers |
310 | ASTContext Context(OpenCLLangOpts, SourceMgr, PP.getIdentifierTable(), |
311 | PP.getSelectorTable(), PP.getBuiltinInfo(), PP.TUKind); |
312 | Context.InitBuiltinTypes(Target: *Target); |
313 | |
314 | ASTConsumer Consumer; |
315 | Sema S(PP, Context, Consumer); |
316 | Parser P(PP, S, false); |
317 | PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks; |
318 | PP.addPPCallbacks(C: std::unique_ptr<PPCallbacks>(Callbacks)); |
319 | |
320 | // Lex source text. |
321 | PP.EnterMainSourceFile(); |
322 | PP.LexTokensUntilEOF(); |
323 | |
324 | PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = { |
325 | .Name: Callbacks->Name, |
326 | .State: Callbacks->State |
327 | }; |
328 | return RetVal; |
329 | } |
330 | }; |
331 | |
332 | TEST_F(PPCallbacksTest, UserFileCharacteristics) { |
333 | const char *Source = "#include \"quoted.h\"\n"; |
334 | |
335 | SrcMgr::CharacteristicKind Kind = |
336 | InclusionDirectiveCharacteristicKind(SourceText: Source, HeaderPath: "/quoted.h", SystemHeader: false); |
337 | |
338 | ASSERT_EQ(SrcMgr::CharacteristicKind::C_User, Kind); |
339 | } |
340 | |
341 | TEST_F(PPCallbacksTest, QuotedFilename) { |
342 | const char* Source = |
343 | "#include \"quoted.h\"\n"; |
344 | |
345 | CharSourceRange Range = |
346 | InclusionDirectiveFilenameRange(SourceText: Source, HeaderPath: "/quoted.h", SystemHeader: false); |
347 | |
348 | ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); |
349 | } |
350 | |
351 | TEST_F(PPCallbacksTest, AngledFilename) { |
352 | const char* Source = |
353 | "#include <angled.h>\n"; |
354 | |
355 | CharSourceRange Range = |
356 | InclusionDirectiveFilenameRange(SourceText: Source, HeaderPath: "/angled.h", SystemHeader: true); |
357 | |
358 | ASSERT_EQ("<angled.h>", GetSourceString(Range)); |
359 | } |
360 | |
361 | TEST_F(PPCallbacksTest, QuotedInMacro) { |
362 | const char* Source = |
363 | "#define MACRO_QUOTED \"quoted.h\"\n" |
364 | "#include MACRO_QUOTED\n"; |
365 | |
366 | CharSourceRange Range = |
367 | InclusionDirectiveFilenameRange(SourceText: Source, HeaderPath: "/quoted.h", SystemHeader: false); |
368 | |
369 | ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); |
370 | } |
371 | |
372 | TEST_F(PPCallbacksTest, AngledInMacro) { |
373 | const char* Source = |
374 | "#define MACRO_ANGLED <angled.h>\n" |
375 | "#include MACRO_ANGLED\n"; |
376 | |
377 | CharSourceRange Range = |
378 | InclusionDirectiveFilenameRange(SourceText: Source, HeaderPath: "/angled.h", SystemHeader: true); |
379 | |
380 | ASSERT_EQ("<angled.h>", GetSourceString(Range)); |
381 | } |
382 | |
383 | TEST_F(PPCallbacksTest, StringizedMacroArgument) { |
384 | const char* Source = |
385 | "#define MACRO_STRINGIZED(x) #x\n" |
386 | "#include MACRO_STRINGIZED(quoted.h)\n"; |
387 | |
388 | CharSourceRange Range = |
389 | InclusionDirectiveFilenameRange(SourceText: Source, HeaderPath: "/quoted.h", SystemHeader: false); |
390 | |
391 | ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); |
392 | } |
393 | |
394 | TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) { |
395 | const char* Source = |
396 | "#define MACRO_ANGLED <angled.h>\n" |
397 | "#define MACRO_CONCAT(x, y) x ## _ ## y\n" |
398 | "#include MACRO_CONCAT(MACRO, ANGLED)\n"; |
399 | |
400 | CharSourceRange Range = |
401 | InclusionDirectiveFilenameRange(SourceText: Source, HeaderPath: "/angled.h", SystemHeader: false); |
402 | |
403 | ASSERT_EQ("<angled.h>", GetSourceString(Range)); |
404 | } |
405 | |
406 | TEST_F(PPCallbacksTest, TrigraphFilename) { |
407 | const char* Source = |
408 | "#include \"tri\?\?-graph.h\"\n"; |
409 | |
410 | CharSourceRange Range = |
411 | InclusionDirectiveFilenameRange(SourceText: Source, HeaderPath: "/tri~graph.h", SystemHeader: false); |
412 | |
413 | ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); |
414 | } |
415 | |
416 | TEST_F(PPCallbacksTest, TrigraphInMacro) { |
417 | const char* Source = |
418 | "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n" |
419 | "#include MACRO_TRIGRAPH\n"; |
420 | |
421 | CharSourceRange Range = |
422 | InclusionDirectiveFilenameRange(SourceText: Source, HeaderPath: "/tri~graph.h", SystemHeader: false); |
423 | |
424 | ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); |
425 | } |
426 | |
427 | TEST_F(PPCallbacksTest, FileNotFoundSkipped) { |
428 | const char *SourceText = "#include \"skipped.h\"\n"; |
429 | |
430 | std::unique_ptr<llvm::MemoryBuffer> SourceBuf = |
431 | llvm::MemoryBuffer::getMemBuffer(InputData: SourceText); |
432 | SourceMgr.setMainFileID(SourceMgr.createFileID(Buffer: std::move(SourceBuf))); |
433 | |
434 | HeaderSearchOptions HSOpts; |
435 | TrivialModuleLoader ModLoader; |
436 | PreprocessorOptions PPOpts; |
437 | HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get()); |
438 | |
439 | DiagnosticConsumer *DiagConsumer = new DiagnosticConsumer; |
440 | DiagnosticsEngine FileNotFoundDiags(DiagID, DiagOpts, DiagConsumer); |
441 | Preprocessor PP(PPOpts, FileNotFoundDiags, LangOpts, SourceMgr, HeaderInfo, |
442 | ModLoader, /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); |
443 | PP.Initialize(Target: *Target); |
444 | |
445 | class FileNotFoundCallbacks : public PPCallbacks { |
446 | public: |
447 | unsigned int NumCalls = 0; |
448 | bool FileNotFound(StringRef FileName) override { |
449 | NumCalls++; |
450 | return FileName == "skipped.h"; |
451 | } |
452 | }; |
453 | |
454 | auto *Callbacks = new FileNotFoundCallbacks; |
455 | PP.addPPCallbacks(C: std::unique_ptr<PPCallbacks>(Callbacks)); |
456 | |
457 | // Lex source text. |
458 | PP.EnterMainSourceFile(); |
459 | PP.LexTokensUntilEOF(); |
460 | |
461 | ASSERT_EQ(1u, Callbacks->NumCalls); |
462 | ASSERT_EQ(0u, DiagConsumer->getNumErrors()); |
463 | } |
464 | |
465 | TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) { |
466 | const char* Source = |
467 | "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"; |
468 | |
469 | PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters = |
470 | PragmaOpenCLExtensionCall(SourceText: Source); |
471 | |
472 | ASSERT_EQ("cl_khr_fp64", Parameters.Name); |
473 | unsigned ExpectedState = 1; |
474 | ASSERT_EQ(ExpectedState, Parameters.State); |
475 | } |
476 | |
477 | TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) { |
478 | const char* Source = |
479 | "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n"; |
480 | |
481 | PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters = |
482 | PragmaOpenCLExtensionCall(SourceText: Source); |
483 | |
484 | ASSERT_EQ("cl_khr_fp16", Parameters.Name); |
485 | unsigned ExpectedState = 0; |
486 | ASSERT_EQ(ExpectedState, Parameters.State); |
487 | } |
488 | |
489 | TEST_F(PPCallbacksTest, CollectMarks) { |
490 | const char *Source = |
491 | "#pragma mark\n" |
492 | "#pragma mark\r\n" |
493 | "#pragma mark - trivia\n" |
494 | "#pragma mark - trivia\r\n"; |
495 | |
496 | auto Marks = PragmaMarkCall(SourceText: Source); |
497 | |
498 | ASSERT_EQ(4u, Marks.size()); |
499 | ASSERT_TRUE(Marks[0].Trivia.empty()); |
500 | ASSERT_TRUE(Marks[1].Trivia.empty()); |
501 | ASSERT_FALSE(Marks[2].Trivia.empty()); |
502 | ASSERT_FALSE(Marks[3].Trivia.empty()); |
503 | ASSERT_EQ(" - trivia", Marks[2].Trivia); |
504 | ASSERT_EQ(" - trivia", Marks[3].Trivia); |
505 | } |
506 | |
507 | TEST_F(PPCallbacksTest, DirectiveExprRanges) { |
508 | const auto &Results1 = DirectiveExprRange(SourceText: "#if FLUZZY_FLOOF\n#endif\n"); |
509 | EXPECT_EQ(Results1.size(), 1U); |
510 | EXPECT_EQ( |
511 | GetSourceStringToEnd(CharSourceRange(Results1[0].ConditionRange, false)), |
512 | "FLUZZY_FLOOF"); |
513 | |
514 | const auto &Results2 = DirectiveExprRange(SourceText: "#if 1 + 4 < 7\n#endif\n"); |
515 | EXPECT_EQ(Results2.size(), 1U); |
516 | EXPECT_EQ( |
517 | GetSourceStringToEnd(CharSourceRange(Results2[0].ConditionRange, false)), |
518 | "1 + 4 < 7"); |
519 | |
520 | const auto &Results3 = DirectiveExprRange(SourceText: "#if 1 + \\\n 2\n#endif\n"); |
521 | EXPECT_EQ(Results3.size(), 1U); |
522 | EXPECT_EQ( |
523 | GetSourceStringToEnd(CharSourceRange(Results3[0].ConditionRange, false)), |
524 | "1 + \\\n 2"); |
525 | |
526 | const auto &Results4 = DirectiveExprRange(SourceText: "#if 0\n#elif FLOOFY\n#endif\n"); |
527 | EXPECT_EQ(Results4.size(), 2U); |
528 | EXPECT_EQ( |
529 | GetSourceStringToEnd(CharSourceRange(Results4[0].ConditionRange, false)), |
530 | "0"); |
531 | EXPECT_EQ( |
532 | GetSourceStringToEnd(CharSourceRange(Results4[1].ConditionRange, false)), |
533 | "FLOOFY"); |
534 | |
535 | const auto &Results5 = DirectiveExprRange(SourceText: "#if 1\n#elif FLOOFY\n#endif\n"); |
536 | EXPECT_EQ(Results5.size(), 2U); |
537 | EXPECT_EQ( |
538 | GetSourceStringToEnd(CharSourceRange(Results5[0].ConditionRange, false)), |
539 | "1"); |
540 | EXPECT_EQ( |
541 | GetSourceStringToEnd(CharSourceRange(Results5[1].ConditionRange, false)), |
542 | "FLOOFY"); |
543 | |
544 | const auto &Results6 = |
545 | DirectiveExprRange(SourceText: "#if defined(FLUZZY_FLOOF)\n#endif\n"); |
546 | EXPECT_EQ(Results6.size(), 1U); |
547 | EXPECT_EQ( |
548 | GetSourceStringToEnd(CharSourceRange(Results6[0].ConditionRange, false)), |
549 | "defined(FLUZZY_FLOOF)"); |
550 | |
551 | const auto &Results7 = |
552 | DirectiveExprRange(SourceText: "#if 1\n#elif defined(FLOOFY)\n#endif\n"); |
553 | EXPECT_EQ(Results7.size(), 2U); |
554 | EXPECT_EQ( |
555 | GetSourceStringToEnd(CharSourceRange(Results7[0].ConditionRange, false)), |
556 | "1"); |
557 | EXPECT_EQ( |
558 | GetSourceStringToEnd(CharSourceRange(Results7[1].ConditionRange, false)), |
559 | "defined(FLOOFY)"); |
560 | |
561 | const auto &Results8 = |
562 | DirectiveExprRange(SourceText: "#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n"); |
563 | EXPECT_EQ(Results8.size(), 1U); |
564 | EXPECT_EQ( |
565 | GetSourceStringToEnd(CharSourceRange(Results8[0].ConditionRange, false)), |
566 | "__FILE__ > FLOOFY"); |
567 | EXPECT_EQ( |
568 | Lexer::getSourceText(CharSourceRange(Results8[0].ConditionRange, false), |
569 | SourceMgr, LangOpts), |
570 | "__FILE__ > FLOOFY"); |
571 | |
572 | const char *MultiExprIf = "#if defined(FLOOFY) || defined(FLUZZY)\n#endif\n"; |
573 | const auto &Results9 = DirectiveExprRange(SourceText: MultiExprIf); |
574 | EXPECT_EQ(Results9.size(), 1U); |
575 | EXPECT_EQ( |
576 | Lexer::getSourceText(CharSourceRange(Results9[0].ConditionRange, false), |
577 | SourceMgr, LangOpts), |
578 | "defined(FLOOFY) || defined(FLUZZY)"); |
579 | |
580 | PreprocessorOptions PPOptsSingleFileParse; |
581 | PPOptsSingleFileParse.SingleFileParseMode = true; |
582 | const auto &Results10 = |
583 | DirectiveExprRange(SourceText: MultiExprIf, PPOpts: PPOptsSingleFileParse); |
584 | EXPECT_EQ(Results10.size(), 1U); |
585 | EXPECT_EQ( |
586 | Lexer::getSourceText(CharSourceRange(Results10[0].ConditionRange, false), |
587 | SourceMgr, LangOpts), |
588 | "defined(FLOOFY) || defined(FLUZZY)"); |
589 | } |
590 | |
591 | } // namespace |
592 |
Definitions
- InclusionDirectiveCallbacks
- InclusionDirective
- CondDirectiveCallbacks
- Result
- Result
- If
- Elif
- PragmaOpenCLExtensionCallbacks
- PragmaOpenCLExtensionCallbacks
- PragmaOpenCLExtension
- PragmaMarkCallbacks
- Mark
- PragmaMark
- PPCallbacksTest
- PPCallbacksTest
- AddFakeHeader
- GetSourceString
- GetSourceStringToEnd
- InclusionDirectiveFilenameRange
- InclusionDirectiveCharacteristicKind
- InclusionDirectiveCallback
- DirectiveExprRange
- PragmaMarkCall
Learn to use CMake with our Intro Training
Find out more