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
*SuggestedModule
,
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
;
49 this->SearchPath
= SearchPath
.str();
50 this->RelativePath
= RelativePath
.str();
51 this->SuggestedModule
= SuggestedModule
;
52 this->ModuleImported
= ModuleImported
;
53 this->FileType
= FileType
;
56 SourceLocation HashLoc
;
58 SmallString
<16> FileName
;
60 CharSourceRange FilenameRange
;
61 OptionalFileEntryRef File
;
62 SmallString
<16> SearchPath
;
63 SmallString
<16> RelativePath
;
64 const Module
*SuggestedModule
;
66 SrcMgr::CharacteristicKind FileType
;
69 class CondDirectiveCallbacks
: public PPCallbacks
{
72 SourceRange ConditionRange
;
73 ConditionValueKind ConditionValue
;
75 Result(SourceRange R
, ConditionValueKind K
)
76 : ConditionRange(R
), ConditionValue(K
) {}
79 std::vector
<Result
> Results
;
81 void If(SourceLocation Loc
, SourceRange ConditionRange
,
82 ConditionValueKind ConditionValue
) override
{
83 Results
.emplace_back(ConditionRange
, ConditionValue
);
86 void Elif(SourceLocation Loc
, SourceRange ConditionRange
,
87 ConditionValueKind ConditionValue
, SourceLocation IfLoc
) override
{
88 Results
.emplace_back(ConditionRange
, ConditionValue
);
92 // Stub to collect data from PragmaOpenCLExtension callbacks.
93 class PragmaOpenCLExtensionCallbacks
: public PPCallbacks
{
100 PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {}
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
;
112 SourceLocation NameLoc
;
113 SmallString
<16> Name
;
114 SourceLocation StateLoc
;
118 class PragmaMarkCallbacks
: public PPCallbacks
{
121 SourceLocation Location
;
125 std::vector
<Mark
> Marks
;
127 void PragmaMark(SourceLocation Loc
, StringRef Trivia
) override
{
128 Marks
.emplace_back(Mark
{Loc
, Trivia
.str()});
132 // PPCallbacks test fixture.
133 class PPCallbacksTest
: public ::testing::Test
{
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
, TargetOpts
);
145 IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem
;
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
;
155 // Register a header path as a known file and add its location
157 void AddFakeHeader(HeaderSearch
&HeaderInfo
, const char *HeaderPath
,
158 bool IsSystemHeader
) {
159 // Tell FileMgr about header.
160 InMemoryFileSystem
->addFile(HeaderPath
, 0,
161 llvm::MemoryBuffer::getMemBuffer("\n"));
163 // Add header's parent path to search path.
164 StringRef SearchPath
= llvm::sys::path::parent_path(HeaderPath
);
165 auto DE
= FileMgr
.getOptionalDirectoryRef(SearchPath
);
166 DirectoryLookup
DL(*DE
, SrcMgr::C_User
, false);
167 HeaderInfo
.AddSearchPath(DL
, IsSystemHeader
);
170 // Get the raw source string of the range.
171 StringRef
GetSourceString(CharSourceRange Range
) {
172 const char* B
= SourceMgr
.getCharacterData(Range
.getBegin());
173 const char* E
= SourceMgr
.getCharacterData(Range
.getEnd());
175 return StringRef(B
, E
- B
);
178 StringRef
GetSourceStringToEnd(CharSourceRange Range
) {
179 const char *B
= SourceMgr
.getCharacterData(Range
.getBegin());
180 const char *E
= SourceMgr
.getCharacterData(Range
.getEnd());
184 E
- B
+ Lexer::MeasureTokenLength(Range
.getEnd(), SourceMgr
, LangOpts
));
187 // Run lexer over SourceText and collect FilenameRange from
188 // the InclusionDirective callback.
189 CharSourceRange
InclusionDirectiveFilenameRange(const char *SourceText
,
190 const char *HeaderPath
,
192 std::unique_ptr
<llvm::MemoryBuffer
> Buf
=
193 llvm::MemoryBuffer::getMemBuffer(SourceText
);
194 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(Buf
)));
196 TrivialModuleLoader ModLoader
;
198 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
199 Diags
, LangOpts
, Target
.get());
200 AddFakeHeader(HeaderInfo
, HeaderPath
, SystemHeader
);
202 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
, LangOpts
,
203 SourceMgr
, HeaderInfo
, ModLoader
,
204 /*IILookup =*/nullptr,
205 /*OwnsHeaderSearch =*/false);
206 return InclusionDirectiveCallback(PP
)->FilenameRange
;
209 SrcMgr::CharacteristicKind
InclusionDirectiveCharacteristicKind(
210 const char *SourceText
, const char *HeaderPath
, bool SystemHeader
) {
211 std::unique_ptr
<llvm::MemoryBuffer
> Buf
=
212 llvm::MemoryBuffer::getMemBuffer(SourceText
);
213 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(Buf
)));
215 TrivialModuleLoader ModLoader
;
217 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
218 Diags
, LangOpts
, Target
.get());
219 AddFakeHeader(HeaderInfo
, HeaderPath
, SystemHeader
);
221 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
, LangOpts
,
222 SourceMgr
, HeaderInfo
, ModLoader
,
223 /*IILookup =*/nullptr,
224 /*OwnsHeaderSearch =*/false);
225 return InclusionDirectiveCallback(PP
)->FileType
;
228 InclusionDirectiveCallbacks
*InclusionDirectiveCallback(Preprocessor
&PP
) {
229 PP
.Initialize(*Target
);
230 InclusionDirectiveCallbacks
* Callbacks
= new InclusionDirectiveCallbacks
;
231 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
234 PP
.EnterMainSourceFile();
235 PP
.LexTokensUntilEOF();
237 // Callbacks have been executed at this point -- return filename range.
241 std::vector
<CondDirectiveCallbacks::Result
>
242 DirectiveExprRange(StringRef SourceText
) {
243 TrivialModuleLoader ModLoader
;
244 std::unique_ptr
<llvm::MemoryBuffer
> Buf
=
245 llvm::MemoryBuffer::getMemBuffer(SourceText
);
246 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(Buf
)));
247 HeaderSearch
HeaderInfo(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
);
254 auto *Callbacks
= new CondDirectiveCallbacks
;
255 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
258 PP
.EnterMainSourceFile();
259 PP
.LexTokensUntilEOF();
261 return Callbacks
->Results
;
264 std::vector
<PragmaMarkCallbacks::Mark
>
265 PragmaMarkCall(const char *SourceText
) {
266 std::unique_ptr
<llvm::MemoryBuffer
> SourceBuf
=
267 llvm::MemoryBuffer::getMemBuffer(SourceText
, "test.c");
268 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(SourceBuf
)));
270 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
271 Diags
, LangOpts
, Target
.get());
272 TrivialModuleLoader ModLoader
;
274 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
, LangOpts
,
275 SourceMgr
, HeaderInfo
, ModLoader
, /*IILookup=*/nullptr,
276 /*OwnsHeaderSearch=*/false);
277 PP
.Initialize(*Target
);
279 auto *Callbacks
= new PragmaMarkCallbacks
;
280 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
283 PP
.EnterMainSourceFile();
284 PP
.LexTokensUntilEOF();
286 return Callbacks
->Marks
;
289 PragmaOpenCLExtensionCallbacks::CallbackParameters
290 PragmaOpenCLExtensionCall(const char *SourceText
) {
291 LangOptions OpenCLLangOpts
;
292 OpenCLLangOpts
.OpenCL
= 1;
294 std::unique_ptr
<llvm::MemoryBuffer
> SourceBuf
=
295 llvm::MemoryBuffer::getMemBuffer(SourceText
, "test.cl");
296 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(SourceBuf
)));
298 TrivialModuleLoader ModLoader
;
299 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
300 Diags
, OpenCLLangOpts
, Target
.get());
302 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
,
303 OpenCLLangOpts
, SourceMgr
, HeaderInfo
, ModLoader
,
304 /*IILookup =*/nullptr,
305 /*OwnsHeaderSearch =*/false);
306 PP
.Initialize(*Target
);
308 // parser actually sets correct pragma handlers for preprocessor
309 // according to LangOptions, so we init Parser to register opencl
311 ASTContext
Context(OpenCLLangOpts
, SourceMgr
, PP
.getIdentifierTable(),
312 PP
.getSelectorTable(), PP
.getBuiltinInfo(), PP
.TUKind
);
313 Context
.InitBuiltinTypes(*Target
);
315 ASTConsumer Consumer
;
316 Sema
S(PP
, Context
, Consumer
);
317 Parser
P(PP
, S
, false);
318 PragmaOpenCLExtensionCallbacks
* Callbacks
= new PragmaOpenCLExtensionCallbacks
;
319 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
322 PP
.EnterMainSourceFile();
323 PP
.LexTokensUntilEOF();
325 PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal
= {
333 TEST_F(PPCallbacksTest
, UserFileCharacteristics
) {
334 const char *Source
= "#include \"quoted.h\"\n";
336 SrcMgr::CharacteristicKind Kind
=
337 InclusionDirectiveCharacteristicKind(Source
, "/quoted.h", false);
339 ASSERT_EQ(SrcMgr::CharacteristicKind::C_User
, Kind
);
342 TEST_F(PPCallbacksTest
, QuotedFilename
) {
344 "#include \"quoted.h\"\n";
346 CharSourceRange Range
=
347 InclusionDirectiveFilenameRange(Source
, "/quoted.h", false);
349 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range
));
352 TEST_F(PPCallbacksTest
, AngledFilename
) {
354 "#include <angled.h>\n";
356 CharSourceRange Range
=
357 InclusionDirectiveFilenameRange(Source
, "/angled.h", true);
359 ASSERT_EQ("<angled.h>", GetSourceString(Range
));
362 TEST_F(PPCallbacksTest
, QuotedInMacro
) {
364 "#define MACRO_QUOTED \"quoted.h\"\n"
365 "#include MACRO_QUOTED\n";
367 CharSourceRange Range
=
368 InclusionDirectiveFilenameRange(Source
, "/quoted.h", false);
370 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range
));
373 TEST_F(PPCallbacksTest
, AngledInMacro
) {
375 "#define MACRO_ANGLED <angled.h>\n"
376 "#include MACRO_ANGLED\n";
378 CharSourceRange Range
=
379 InclusionDirectiveFilenameRange(Source
, "/angled.h", true);
381 ASSERT_EQ("<angled.h>", GetSourceString(Range
));
384 TEST_F(PPCallbacksTest
, StringizedMacroArgument
) {
386 "#define MACRO_STRINGIZED(x) #x\n"
387 "#include MACRO_STRINGIZED(quoted.h)\n";
389 CharSourceRange Range
=
390 InclusionDirectiveFilenameRange(Source
, "/quoted.h", false);
392 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range
));
395 TEST_F(PPCallbacksTest
, ConcatenatedMacroArgument
) {
397 "#define MACRO_ANGLED <angled.h>\n"
398 "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
399 "#include MACRO_CONCAT(MACRO, ANGLED)\n";
401 CharSourceRange Range
=
402 InclusionDirectiveFilenameRange(Source
, "/angled.h", false);
404 ASSERT_EQ("<angled.h>", GetSourceString(Range
));
407 TEST_F(PPCallbacksTest
, TrigraphFilename
) {
409 "#include \"tri\?\?-graph.h\"\n";
411 CharSourceRange Range
=
412 InclusionDirectiveFilenameRange(Source
, "/tri~graph.h", false);
414 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range
));
417 TEST_F(PPCallbacksTest
, TrigraphInMacro
) {
419 "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
420 "#include MACRO_TRIGRAPH\n";
422 CharSourceRange Range
=
423 InclusionDirectiveFilenameRange(Source
, "/tri~graph.h", false);
425 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range
));
428 TEST_F(PPCallbacksTest
, FileNotFoundSkipped
) {
429 const char *SourceText
= "#include \"skipped.h\"\n";
431 std::unique_ptr
<llvm::MemoryBuffer
> SourceBuf
=
432 llvm::MemoryBuffer::getMemBuffer(SourceText
);
433 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(SourceBuf
)));
435 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
436 Diags
, LangOpts
, Target
.get());
437 TrivialModuleLoader ModLoader
;
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
);
447 class FileNotFoundCallbacks
: public PPCallbacks
{
449 unsigned int NumCalls
= 0;
450 bool FileNotFound(StringRef FileName
) override
{
452 return FileName
== "skipped.h";
456 auto *Callbacks
= new FileNotFoundCallbacks
;
457 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
460 PP
.EnterMainSourceFile();
461 PP
.LexTokensUntilEOF();
463 ASSERT_EQ(1u, Callbacks
->NumCalls
);
464 ASSERT_EQ(0u, DiagConsumer
->getNumErrors());
467 TEST_F(PPCallbacksTest
, OpenCLExtensionPragmaEnabled
) {
469 "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n";
471 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters
=
472 PragmaOpenCLExtensionCall(Source
);
474 ASSERT_EQ("cl_khr_fp64", Parameters
.Name
);
475 unsigned ExpectedState
= 1;
476 ASSERT_EQ(ExpectedState
, Parameters
.State
);
479 TEST_F(PPCallbacksTest
, OpenCLExtensionPragmaDisabled
) {
481 "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n";
483 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters
=
484 PragmaOpenCLExtensionCall(Source
);
486 ASSERT_EQ("cl_khr_fp16", Parameters
.Name
);
487 unsigned ExpectedState
= 0;
488 ASSERT_EQ(ExpectedState
, Parameters
.State
);
491 TEST_F(PPCallbacksTest
, CollectMarks
) {
495 "#pragma mark - trivia\n"
496 "#pragma mark - trivia\r\n";
498 auto Marks
= PragmaMarkCall(Source
);
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
);
509 TEST_F(PPCallbacksTest
, DirectiveExprRanges
) {
510 const auto &Results1
= DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n");
511 EXPECT_EQ(Results1
.size(), 1U);
513 GetSourceStringToEnd(CharSourceRange(Results1
[0].ConditionRange
, false)),
516 const auto &Results2
= DirectiveExprRange("#if 1 + 4 < 7\n#endif\n");
517 EXPECT_EQ(Results2
.size(), 1U);
519 GetSourceStringToEnd(CharSourceRange(Results2
[0].ConditionRange
, false)),
522 const auto &Results3
= DirectiveExprRange("#if 1 + \\\n 2\n#endif\n");
523 EXPECT_EQ(Results3
.size(), 1U);
525 GetSourceStringToEnd(CharSourceRange(Results3
[0].ConditionRange
, false)),
528 const auto &Results4
= DirectiveExprRange("#if 0\n#elif FLOOFY\n#endif\n");
529 EXPECT_EQ(Results4
.size(), 2U);
531 GetSourceStringToEnd(CharSourceRange(Results4
[0].ConditionRange
, false)),
534 GetSourceStringToEnd(CharSourceRange(Results4
[1].ConditionRange
, false)),
537 const auto &Results5
= DirectiveExprRange("#if 1\n#elif FLOOFY\n#endif\n");
538 EXPECT_EQ(Results5
.size(), 2U);
540 GetSourceStringToEnd(CharSourceRange(Results5
[0].ConditionRange
, false)),
543 GetSourceStringToEnd(CharSourceRange(Results5
[1].ConditionRange
, false)),
546 const auto &Results6
=
547 DirectiveExprRange("#if defined(FLUZZY_FLOOF)\n#endif\n");
548 EXPECT_EQ(Results6
.size(), 1U);
550 GetSourceStringToEnd(CharSourceRange(Results6
[0].ConditionRange
, false)),
551 "defined(FLUZZY_FLOOF)");
553 const auto &Results7
=
554 DirectiveExprRange("#if 1\n#elif defined(FLOOFY)\n#endif\n");
555 EXPECT_EQ(Results7
.size(), 2U);
557 GetSourceStringToEnd(CharSourceRange(Results7
[0].ConditionRange
, false)),
560 GetSourceStringToEnd(CharSourceRange(Results7
[1].ConditionRange
, false)),
563 const auto &Results8
=
564 DirectiveExprRange("#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n");
565 EXPECT_EQ(Results8
.size(), 1U);
567 GetSourceStringToEnd(CharSourceRange(Results8
[0].ConditionRange
, false)),
568 "__FILE__ > FLOOFY");
570 Lexer::getSourceText(CharSourceRange(Results8
[0].ConditionRange
, false),
571 SourceMgr
, LangOpts
),
572 "__FILE__ > FLOOFY");