1 //===- unittests/Lex/PPCallbacksTest.cpp - PPCallbacks tests ------===//
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
7 //===--------------------------------------------------------------===//
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"
29 using namespace clang
;
33 // Stub to collect data from InclusionDirective callbacks.
34 class InclusionDirectiveCallbacks
: public PPCallbacks
{
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
*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
;
48 this->SearchPath
= SearchPath
.str();
49 this->RelativePath
= RelativePath
.str();
50 this->Imported
= Imported
;
51 this->FileType
= FileType
;
54 SourceLocation HashLoc
;
56 SmallString
<16> FileName
;
58 CharSourceRange FilenameRange
;
59 OptionalFileEntryRef File
;
60 SmallString
<16> SearchPath
;
61 SmallString
<16> RelativePath
;
62 const Module
* Imported
;
63 SrcMgr::CharacteristicKind FileType
;
66 class CondDirectiveCallbacks
: public PPCallbacks
{
69 SourceRange ConditionRange
;
70 ConditionValueKind ConditionValue
;
72 Result(SourceRange R
, ConditionValueKind K
)
73 : ConditionRange(R
), ConditionValue(K
) {}
76 std::vector
<Result
> Results
;
78 void If(SourceLocation Loc
, SourceRange ConditionRange
,
79 ConditionValueKind ConditionValue
) override
{
80 Results
.emplace_back(ConditionRange
, ConditionValue
);
83 void Elif(SourceLocation Loc
, SourceRange ConditionRange
,
84 ConditionValueKind ConditionValue
, SourceLocation IfLoc
) override
{
85 Results
.emplace_back(ConditionRange
, ConditionValue
);
89 // Stub to collect data from PragmaOpenCLExtension callbacks.
90 class PragmaOpenCLExtensionCallbacks
: public PPCallbacks
{
97 PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {}
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
;
109 SourceLocation NameLoc
;
110 SmallString
<16> Name
;
111 SourceLocation StateLoc
;
115 class PragmaMarkCallbacks
: public PPCallbacks
{
118 SourceLocation Location
;
122 std::vector
<Mark
> Marks
;
124 void PragmaMark(SourceLocation Loc
, StringRef Trivia
) override
{
125 Marks
.emplace_back(Mark
{Loc
, Trivia
.str()});
129 // PPCallbacks test fixture.
130 class PPCallbacksTest
: public ::testing::Test
{
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
);
142 IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem
;
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
;
152 // Register a header path as a known file and add its location
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"));
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
);
167 // Get the raw source string of the range.
168 StringRef
GetSourceString(CharSourceRange Range
) {
169 const char* B
= SourceMgr
.getCharacterData(Range
.getBegin());
170 const char* E
= SourceMgr
.getCharacterData(Range
.getEnd());
172 return StringRef(B
, E
- B
);
175 StringRef
GetSourceStringToEnd(CharSourceRange Range
) {
176 const char *B
= SourceMgr
.getCharacterData(Range
.getBegin());
177 const char *E
= SourceMgr
.getCharacterData(Range
.getEnd());
181 E
- B
+ Lexer::MeasureTokenLength(Range
.getEnd(), SourceMgr
, LangOpts
));
184 // Run lexer over SourceText and collect FilenameRange from
185 // the InclusionDirective callback.
186 CharSourceRange
InclusionDirectiveFilenameRange(const char *SourceText
,
187 const char *HeaderPath
,
189 std::unique_ptr
<llvm::MemoryBuffer
> Buf
=
190 llvm::MemoryBuffer::getMemBuffer(SourceText
);
191 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(Buf
)));
193 TrivialModuleLoader ModLoader
;
195 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
196 Diags
, LangOpts
, Target
.get());
197 AddFakeHeader(HeaderInfo
, HeaderPath
, SystemHeader
);
199 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
, LangOpts
,
200 SourceMgr
, HeaderInfo
, ModLoader
,
201 /*IILookup =*/nullptr,
202 /*OwnsHeaderSearch =*/false);
203 return InclusionDirectiveCallback(PP
)->FilenameRange
;
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
)));
212 TrivialModuleLoader ModLoader
;
214 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
215 Diags
, LangOpts
, Target
.get());
216 AddFakeHeader(HeaderInfo
, HeaderPath
, SystemHeader
);
218 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
, LangOpts
,
219 SourceMgr
, HeaderInfo
, ModLoader
,
220 /*IILookup =*/nullptr,
221 /*OwnsHeaderSearch =*/false);
222 return InclusionDirectiveCallback(PP
)->FileType
;
225 InclusionDirectiveCallbacks
*InclusionDirectiveCallback(Preprocessor
&PP
) {
226 PP
.Initialize(*Target
);
227 InclusionDirectiveCallbacks
* Callbacks
= new InclusionDirectiveCallbacks
;
228 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
231 PP
.EnterMainSourceFile();
232 PP
.LexTokensUntilEOF();
234 // Callbacks have been executed at this point -- return filename range.
238 std::vector
<CondDirectiveCallbacks::Result
>
239 DirectiveExprRange(StringRef SourceText
) {
240 TrivialModuleLoader ModLoader
;
241 std::unique_ptr
<llvm::MemoryBuffer
> Buf
=
242 llvm::MemoryBuffer::getMemBuffer(SourceText
);
243 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(Buf
)));
244 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
245 Diags
, LangOpts
, Target
.get());
246 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
, LangOpts
,
247 SourceMgr
, HeaderInfo
, ModLoader
,
248 /*IILookup =*/nullptr,
249 /*OwnsHeaderSearch =*/false);
250 PP
.Initialize(*Target
);
251 auto *Callbacks
= new CondDirectiveCallbacks
;
252 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
255 PP
.EnterMainSourceFile();
256 PP
.LexTokensUntilEOF();
258 return Callbacks
->Results
;
261 std::vector
<PragmaMarkCallbacks::Mark
>
262 PragmaMarkCall(const char *SourceText
) {
263 std::unique_ptr
<llvm::MemoryBuffer
> SourceBuf
=
264 llvm::MemoryBuffer::getMemBuffer(SourceText
, "test.c");
265 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(SourceBuf
)));
267 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
268 Diags
, LangOpts
, Target
.get());
269 TrivialModuleLoader ModLoader
;
271 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
, LangOpts
,
272 SourceMgr
, HeaderInfo
, ModLoader
, /*IILookup=*/nullptr,
273 /*OwnsHeaderSearch=*/false);
274 PP
.Initialize(*Target
);
276 auto *Callbacks
= new PragmaMarkCallbacks
;
277 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
280 PP
.EnterMainSourceFile();
281 PP
.LexTokensUntilEOF();
283 return Callbacks
->Marks
;
286 PragmaOpenCLExtensionCallbacks::CallbackParameters
287 PragmaOpenCLExtensionCall(const char *SourceText
) {
288 LangOptions OpenCLLangOpts
;
289 OpenCLLangOpts
.OpenCL
= 1;
291 std::unique_ptr
<llvm::MemoryBuffer
> SourceBuf
=
292 llvm::MemoryBuffer::getMemBuffer(SourceText
, "test.cl");
293 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(SourceBuf
)));
295 TrivialModuleLoader ModLoader
;
296 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
297 Diags
, OpenCLLangOpts
, Target
.get());
299 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
,
300 OpenCLLangOpts
, SourceMgr
, HeaderInfo
, ModLoader
,
301 /*IILookup =*/nullptr,
302 /*OwnsHeaderSearch =*/false);
303 PP
.Initialize(*Target
);
305 // parser actually sets correct pragma handlers for preprocessor
306 // according to LangOptions, so we init Parser to register opencl
308 ASTContext
Context(OpenCLLangOpts
, SourceMgr
, PP
.getIdentifierTable(),
309 PP
.getSelectorTable(), PP
.getBuiltinInfo(), PP
.TUKind
);
310 Context
.InitBuiltinTypes(*Target
);
312 ASTConsumer Consumer
;
313 Sema
S(PP
, Context
, Consumer
);
314 Parser
P(PP
, S
, false);
315 PragmaOpenCLExtensionCallbacks
* Callbacks
= new PragmaOpenCLExtensionCallbacks
;
316 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
319 PP
.EnterMainSourceFile();
320 PP
.LexTokensUntilEOF();
322 PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal
= {
330 TEST_F(PPCallbacksTest
, UserFileCharacteristics
) {
331 const char *Source
= "#include \"quoted.h\"\n";
333 SrcMgr::CharacteristicKind Kind
=
334 InclusionDirectiveCharacteristicKind(Source
, "/quoted.h", false);
336 ASSERT_EQ(SrcMgr::CharacteristicKind::C_User
, Kind
);
339 TEST_F(PPCallbacksTest
, QuotedFilename
) {
341 "#include \"quoted.h\"\n";
343 CharSourceRange Range
=
344 InclusionDirectiveFilenameRange(Source
, "/quoted.h", false);
346 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range
));
349 TEST_F(PPCallbacksTest
, AngledFilename
) {
351 "#include <angled.h>\n";
353 CharSourceRange Range
=
354 InclusionDirectiveFilenameRange(Source
, "/angled.h", true);
356 ASSERT_EQ("<angled.h>", GetSourceString(Range
));
359 TEST_F(PPCallbacksTest
, QuotedInMacro
) {
361 "#define MACRO_QUOTED \"quoted.h\"\n"
362 "#include MACRO_QUOTED\n";
364 CharSourceRange Range
=
365 InclusionDirectiveFilenameRange(Source
, "/quoted.h", false);
367 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range
));
370 TEST_F(PPCallbacksTest
, AngledInMacro
) {
372 "#define MACRO_ANGLED <angled.h>\n"
373 "#include MACRO_ANGLED\n";
375 CharSourceRange Range
=
376 InclusionDirectiveFilenameRange(Source
, "/angled.h", true);
378 ASSERT_EQ("<angled.h>", GetSourceString(Range
));
381 TEST_F(PPCallbacksTest
, StringizedMacroArgument
) {
383 "#define MACRO_STRINGIZED(x) #x\n"
384 "#include MACRO_STRINGIZED(quoted.h)\n";
386 CharSourceRange Range
=
387 InclusionDirectiveFilenameRange(Source
, "/quoted.h", false);
389 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range
));
392 TEST_F(PPCallbacksTest
, ConcatenatedMacroArgument
) {
394 "#define MACRO_ANGLED <angled.h>\n"
395 "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
396 "#include MACRO_CONCAT(MACRO, ANGLED)\n";
398 CharSourceRange Range
=
399 InclusionDirectiveFilenameRange(Source
, "/angled.h", false);
401 ASSERT_EQ("<angled.h>", GetSourceString(Range
));
404 TEST_F(PPCallbacksTest
, TrigraphFilename
) {
406 "#include \"tri\?\?-graph.h\"\n";
408 CharSourceRange Range
=
409 InclusionDirectiveFilenameRange(Source
, "/tri~graph.h", false);
411 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range
));
414 TEST_F(PPCallbacksTest
, TrigraphInMacro
) {
416 "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
417 "#include MACRO_TRIGRAPH\n";
419 CharSourceRange Range
=
420 InclusionDirectiveFilenameRange(Source
, "/tri~graph.h", false);
422 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range
));
425 TEST_F(PPCallbacksTest
, FileNotFoundSkipped
) {
426 const char *SourceText
= "#include \"skipped.h\"\n";
428 std::unique_ptr
<llvm::MemoryBuffer
> SourceBuf
=
429 llvm::MemoryBuffer::getMemBuffer(SourceText
);
430 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(SourceBuf
)));
432 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
433 Diags
, LangOpts
, Target
.get());
434 TrivialModuleLoader ModLoader
;
436 DiagnosticConsumer
*DiagConsumer
= new DiagnosticConsumer
;
437 DiagnosticsEngine
FileNotFoundDiags(DiagID
, DiagOpts
.get(), DiagConsumer
);
438 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), FileNotFoundDiags
,
439 LangOpts
, SourceMgr
, HeaderInfo
, ModLoader
,
440 /*IILookup=*/nullptr,
441 /*OwnsHeaderSearch=*/false);
442 PP
.Initialize(*Target
);
444 class FileNotFoundCallbacks
: public PPCallbacks
{
446 unsigned int NumCalls
= 0;
447 bool FileNotFound(StringRef FileName
) override
{
449 return FileName
== "skipped.h";
453 auto *Callbacks
= new FileNotFoundCallbacks
;
454 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
457 PP
.EnterMainSourceFile();
458 PP
.LexTokensUntilEOF();
460 ASSERT_EQ(1u, Callbacks
->NumCalls
);
461 ASSERT_EQ(0u, DiagConsumer
->getNumErrors());
464 TEST_F(PPCallbacksTest
, OpenCLExtensionPragmaEnabled
) {
466 "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n";
468 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters
=
469 PragmaOpenCLExtensionCall(Source
);
471 ASSERT_EQ("cl_khr_fp64", Parameters
.Name
);
472 unsigned ExpectedState
= 1;
473 ASSERT_EQ(ExpectedState
, Parameters
.State
);
476 TEST_F(PPCallbacksTest
, OpenCLExtensionPragmaDisabled
) {
478 "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n";
480 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters
=
481 PragmaOpenCLExtensionCall(Source
);
483 ASSERT_EQ("cl_khr_fp16", Parameters
.Name
);
484 unsigned ExpectedState
= 0;
485 ASSERT_EQ(ExpectedState
, Parameters
.State
);
488 TEST_F(PPCallbacksTest
, CollectMarks
) {
492 "#pragma mark - trivia\n"
493 "#pragma mark - trivia\r\n";
495 auto Marks
= PragmaMarkCall(Source
);
497 ASSERT_EQ(4u, Marks
.size());
498 ASSERT_TRUE(Marks
[0].Trivia
.empty());
499 ASSERT_TRUE(Marks
[1].Trivia
.empty());
500 ASSERT_FALSE(Marks
[2].Trivia
.empty());
501 ASSERT_FALSE(Marks
[3].Trivia
.empty());
502 ASSERT_EQ(" - trivia", Marks
[2].Trivia
);
503 ASSERT_EQ(" - trivia", Marks
[3].Trivia
);
506 TEST_F(PPCallbacksTest
, DirectiveExprRanges
) {
507 const auto &Results1
= DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n");
508 EXPECT_EQ(Results1
.size(), 1U);
510 GetSourceStringToEnd(CharSourceRange(Results1
[0].ConditionRange
, false)),
513 const auto &Results2
= DirectiveExprRange("#if 1 + 4 < 7\n#endif\n");
514 EXPECT_EQ(Results2
.size(), 1U);
516 GetSourceStringToEnd(CharSourceRange(Results2
[0].ConditionRange
, false)),
519 const auto &Results3
= DirectiveExprRange("#if 1 + \\\n 2\n#endif\n");
520 EXPECT_EQ(Results3
.size(), 1U);
522 GetSourceStringToEnd(CharSourceRange(Results3
[0].ConditionRange
, false)),
525 const auto &Results4
= DirectiveExprRange("#if 0\n#elif FLOOFY\n#endif\n");
526 EXPECT_EQ(Results4
.size(), 2U);
528 GetSourceStringToEnd(CharSourceRange(Results4
[0].ConditionRange
, false)),
531 GetSourceStringToEnd(CharSourceRange(Results4
[1].ConditionRange
, false)),
534 const auto &Results5
= DirectiveExprRange("#if 1\n#elif FLOOFY\n#endif\n");
535 EXPECT_EQ(Results5
.size(), 2U);
537 GetSourceStringToEnd(CharSourceRange(Results5
[0].ConditionRange
, false)),
540 GetSourceStringToEnd(CharSourceRange(Results5
[1].ConditionRange
, false)),
543 const auto &Results6
=
544 DirectiveExprRange("#if defined(FLUZZY_FLOOF)\n#endif\n");
545 EXPECT_EQ(Results6
.size(), 1U);
547 GetSourceStringToEnd(CharSourceRange(Results6
[0].ConditionRange
, false)),
548 "defined(FLUZZY_FLOOF)");
550 const auto &Results7
=
551 DirectiveExprRange("#if 1\n#elif defined(FLOOFY)\n#endif\n");
552 EXPECT_EQ(Results7
.size(), 2U);
554 GetSourceStringToEnd(CharSourceRange(Results7
[0].ConditionRange
, false)),
557 GetSourceStringToEnd(CharSourceRange(Results7
[1].ConditionRange
, false)),
560 const auto &Results8
=
561 DirectiveExprRange("#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n");
562 EXPECT_EQ(Results8
.size(), 1U);
564 GetSourceStringToEnd(CharSourceRange(Results8
[0].ConditionRange
, false)),
565 "__FILE__ > FLOOFY");
567 Lexer::getSourceText(CharSourceRange(Results8
[0].ConditionRange
, false),
568 SourceMgr
, LangOpts
),
569 "__FILE__ > FLOOFY");