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
, const FileEntry
*File
,
39 StringRef SearchPath
, StringRef RelativePath
,
40 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 const FileEntry
* 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 // PPCallbacks test fixture.
116 class PPCallbacksTest
: public ::testing::Test
{
119 : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem
),
120 FileMgr(FileSystemOptions(), InMemoryFileSystem
),
121 DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()),
122 Diags(DiagID
, DiagOpts
.get(), new IgnoringDiagConsumer()),
123 SourceMgr(Diags
, FileMgr
), TargetOpts(new TargetOptions()) {
124 TargetOpts
->Triple
= "x86_64-apple-darwin11.1.0";
125 Target
= TargetInfo::CreateTargetInfo(Diags
, TargetOpts
);
128 IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem
;
130 IntrusiveRefCntPtr
<DiagnosticIDs
> DiagID
;
131 IntrusiveRefCntPtr
<DiagnosticOptions
> DiagOpts
;
132 DiagnosticsEngine Diags
;
133 SourceManager SourceMgr
;
134 LangOptions LangOpts
;
135 std::shared_ptr
<TargetOptions
> TargetOpts
;
136 IntrusiveRefCntPtr
<TargetInfo
> Target
;
138 // Register a header path as a known file and add its location
140 void AddFakeHeader(HeaderSearch
&HeaderInfo
, const char *HeaderPath
,
141 bool IsSystemHeader
) {
142 // Tell FileMgr about header.
143 InMemoryFileSystem
->addFile(HeaderPath
, 0,
144 llvm::MemoryBuffer::getMemBuffer("\n"));
146 // Add header's parent path to search path.
147 StringRef SearchPath
= llvm::sys::path::parent_path(HeaderPath
);
148 auto DE
= FileMgr
.getOptionalDirectoryRef(SearchPath
);
149 DirectoryLookup
DL(*DE
, SrcMgr::C_User
, false);
150 HeaderInfo
.AddSearchPath(DL
, IsSystemHeader
);
153 // Get the raw source string of the range.
154 StringRef
GetSourceString(CharSourceRange Range
) {
155 const char* B
= SourceMgr
.getCharacterData(Range
.getBegin());
156 const char* E
= SourceMgr
.getCharacterData(Range
.getEnd());
158 return StringRef(B
, E
- B
);
161 StringRef
GetSourceStringToEnd(CharSourceRange Range
) {
162 const char *B
= SourceMgr
.getCharacterData(Range
.getBegin());
163 const char *E
= SourceMgr
.getCharacterData(Range
.getEnd());
167 E
- B
+ Lexer::MeasureTokenLength(Range
.getEnd(), SourceMgr
, LangOpts
));
170 // Run lexer over SourceText and collect FilenameRange from
171 // the InclusionDirective callback.
172 CharSourceRange
InclusionDirectiveFilenameRange(const char *SourceText
,
173 const char *HeaderPath
,
175 std::unique_ptr
<llvm::MemoryBuffer
> Buf
=
176 llvm::MemoryBuffer::getMemBuffer(SourceText
);
177 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(Buf
)));
179 TrivialModuleLoader ModLoader
;
181 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
182 Diags
, LangOpts
, Target
.get());
183 AddFakeHeader(HeaderInfo
, HeaderPath
, SystemHeader
);
185 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
, LangOpts
,
186 SourceMgr
, HeaderInfo
, ModLoader
,
187 /*IILookup =*/nullptr,
188 /*OwnsHeaderSearch =*/false);
189 return InclusionDirectiveCallback(PP
)->FilenameRange
;
192 SrcMgr::CharacteristicKind
InclusionDirectiveCharacteristicKind(
193 const char *SourceText
, const char *HeaderPath
, bool SystemHeader
) {
194 std::unique_ptr
<llvm::MemoryBuffer
> Buf
=
195 llvm::MemoryBuffer::getMemBuffer(SourceText
);
196 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(Buf
)));
198 TrivialModuleLoader ModLoader
;
200 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
201 Diags
, LangOpts
, Target
.get());
202 AddFakeHeader(HeaderInfo
, HeaderPath
, SystemHeader
);
204 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
, LangOpts
,
205 SourceMgr
, HeaderInfo
, ModLoader
,
206 /*IILookup =*/nullptr,
207 /*OwnsHeaderSearch =*/false);
208 return InclusionDirectiveCallback(PP
)->FileType
;
211 InclusionDirectiveCallbacks
*InclusionDirectiveCallback(Preprocessor
&PP
) {
212 PP
.Initialize(*Target
);
213 InclusionDirectiveCallbacks
* Callbacks
= new InclusionDirectiveCallbacks
;
214 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
217 PP
.EnterMainSourceFile();
222 if (Tok
.is(tok::eof
))
226 // Callbacks have been executed at this point -- return filename range.
230 std::vector
<CondDirectiveCallbacks::Result
>
231 DirectiveExprRange(StringRef SourceText
) {
232 TrivialModuleLoader ModLoader
;
233 std::unique_ptr
<llvm::MemoryBuffer
> Buf
=
234 llvm::MemoryBuffer::getMemBuffer(SourceText
);
235 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(Buf
)));
236 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
237 Diags
, LangOpts
, Target
.get());
238 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
, LangOpts
,
239 SourceMgr
, HeaderInfo
, ModLoader
,
240 /*IILookup =*/nullptr,
241 /*OwnsHeaderSearch =*/false);
242 PP
.Initialize(*Target
);
243 auto *Callbacks
= new CondDirectiveCallbacks
;
244 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
247 PP
.EnterMainSourceFile();
252 if (Tok
.is(tok::eof
))
256 return Callbacks
->Results
;
259 PragmaOpenCLExtensionCallbacks::CallbackParameters
260 PragmaOpenCLExtensionCall(const char *SourceText
) {
261 LangOptions OpenCLLangOpts
;
262 OpenCLLangOpts
.OpenCL
= 1;
264 std::unique_ptr
<llvm::MemoryBuffer
> SourceBuf
=
265 llvm::MemoryBuffer::getMemBuffer(SourceText
, "test.cl");
266 SourceMgr
.setMainFileID(SourceMgr
.createFileID(std::move(SourceBuf
)));
268 TrivialModuleLoader ModLoader
;
269 HeaderSearch
HeaderInfo(std::make_shared
<HeaderSearchOptions
>(), SourceMgr
,
270 Diags
, OpenCLLangOpts
, Target
.get());
272 Preprocessor
PP(std::make_shared
<PreprocessorOptions
>(), Diags
,
273 OpenCLLangOpts
, SourceMgr
, HeaderInfo
, ModLoader
,
274 /*IILookup =*/nullptr,
275 /*OwnsHeaderSearch =*/false);
276 PP
.Initialize(*Target
);
278 // parser actually sets correct pragma handlers for preprocessor
279 // according to LangOptions, so we init Parser to register opencl
281 ASTContext
Context(OpenCLLangOpts
, SourceMgr
, PP
.getIdentifierTable(),
282 PP
.getSelectorTable(), PP
.getBuiltinInfo());
283 Context
.InitBuiltinTypes(*Target
);
285 ASTConsumer Consumer
;
286 Sema
S(PP
, Context
, Consumer
);
287 Parser
P(PP
, S
, false);
288 PragmaOpenCLExtensionCallbacks
* Callbacks
= new PragmaOpenCLExtensionCallbacks
;
289 PP
.addPPCallbacks(std::unique_ptr
<PPCallbacks
>(Callbacks
));
292 PP
.EnterMainSourceFile();
296 if (Tok
.is(tok::eof
))
300 PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal
= {
308 TEST_F(PPCallbacksTest
, UserFileCharacteristics
) {
309 const char *Source
= "#include \"quoted.h\"\n";
311 SrcMgr::CharacteristicKind Kind
=
312 InclusionDirectiveCharacteristicKind(Source
, "/quoted.h", false);
314 ASSERT_EQ(SrcMgr::CharacteristicKind::C_User
, Kind
);
317 TEST_F(PPCallbacksTest
, QuotedFilename
) {
319 "#include \"quoted.h\"\n";
321 CharSourceRange Range
=
322 InclusionDirectiveFilenameRange(Source
, "/quoted.h", false);
324 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range
));
327 TEST_F(PPCallbacksTest
, AngledFilename
) {
329 "#include <angled.h>\n";
331 CharSourceRange Range
=
332 InclusionDirectiveFilenameRange(Source
, "/angled.h", true);
334 ASSERT_EQ("<angled.h>", GetSourceString(Range
));
337 TEST_F(PPCallbacksTest
, QuotedInMacro
) {
339 "#define MACRO_QUOTED \"quoted.h\"\n"
340 "#include MACRO_QUOTED\n";
342 CharSourceRange Range
=
343 InclusionDirectiveFilenameRange(Source
, "/quoted.h", false);
345 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range
));
348 TEST_F(PPCallbacksTest
, AngledInMacro
) {
350 "#define MACRO_ANGLED <angled.h>\n"
351 "#include MACRO_ANGLED\n";
353 CharSourceRange Range
=
354 InclusionDirectiveFilenameRange(Source
, "/angled.h", true);
356 ASSERT_EQ("<angled.h>", GetSourceString(Range
));
359 TEST_F(PPCallbacksTest
, StringizedMacroArgument
) {
361 "#define MACRO_STRINGIZED(x) #x\n"
362 "#include MACRO_STRINGIZED(quoted.h)\n";
364 CharSourceRange Range
=
365 InclusionDirectiveFilenameRange(Source
, "/quoted.h", false);
367 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range
));
370 TEST_F(PPCallbacksTest
, ConcatenatedMacroArgument
) {
372 "#define MACRO_ANGLED <angled.h>\n"
373 "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
374 "#include MACRO_CONCAT(MACRO, ANGLED)\n";
376 CharSourceRange Range
=
377 InclusionDirectiveFilenameRange(Source
, "/angled.h", false);
379 ASSERT_EQ("<angled.h>", GetSourceString(Range
));
382 TEST_F(PPCallbacksTest
, TrigraphFilename
) {
384 "#include \"tri\?\?-graph.h\"\n";
386 CharSourceRange Range
=
387 InclusionDirectiveFilenameRange(Source
, "/tri~graph.h", false);
389 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range
));
392 TEST_F(PPCallbacksTest
, TrigraphInMacro
) {
394 "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
395 "#include MACRO_TRIGRAPH\n";
397 CharSourceRange Range
=
398 InclusionDirectiveFilenameRange(Source
, "/tri~graph.h", false);
400 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range
));
403 TEST_F(PPCallbacksTest
, OpenCLExtensionPragmaEnabled
) {
405 "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n";
407 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters
=
408 PragmaOpenCLExtensionCall(Source
);
410 ASSERT_EQ("cl_khr_fp64", Parameters
.Name
);
411 unsigned ExpectedState
= 1;
412 ASSERT_EQ(ExpectedState
, Parameters
.State
);
415 TEST_F(PPCallbacksTest
, OpenCLExtensionPragmaDisabled
) {
417 "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n";
419 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters
=
420 PragmaOpenCLExtensionCall(Source
);
422 ASSERT_EQ("cl_khr_fp16", Parameters
.Name
);
423 unsigned ExpectedState
= 0;
424 ASSERT_EQ(ExpectedState
, Parameters
.State
);
427 TEST_F(PPCallbacksTest
, DirectiveExprRanges
) {
428 const auto &Results1
= DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n");
429 EXPECT_EQ(Results1
.size(), 1U);
431 GetSourceStringToEnd(CharSourceRange(Results1
[0].ConditionRange
, false)),
434 const auto &Results2
= DirectiveExprRange("#if 1 + 4 < 7\n#endif\n");
435 EXPECT_EQ(Results2
.size(), 1U);
437 GetSourceStringToEnd(CharSourceRange(Results2
[0].ConditionRange
, false)),
440 const auto &Results3
= DirectiveExprRange("#if 1 + \\\n 2\n#endif\n");
441 EXPECT_EQ(Results3
.size(), 1U);
443 GetSourceStringToEnd(CharSourceRange(Results3
[0].ConditionRange
, false)),
446 const auto &Results4
= DirectiveExprRange("#if 0\n#elif FLOOFY\n#endif\n");
447 EXPECT_EQ(Results4
.size(), 2U);
449 GetSourceStringToEnd(CharSourceRange(Results4
[0].ConditionRange
, false)),
452 GetSourceStringToEnd(CharSourceRange(Results4
[1].ConditionRange
, false)),
455 const auto &Results5
= DirectiveExprRange("#if 1\n#elif FLOOFY\n#endif\n");
456 EXPECT_EQ(Results5
.size(), 2U);
458 GetSourceStringToEnd(CharSourceRange(Results5
[0].ConditionRange
, false)),
461 GetSourceStringToEnd(CharSourceRange(Results5
[1].ConditionRange
, false)),
464 const auto &Results6
=
465 DirectiveExprRange("#if defined(FLUZZY_FLOOF)\n#endif\n");
466 EXPECT_EQ(Results6
.size(), 1U);
468 GetSourceStringToEnd(CharSourceRange(Results6
[0].ConditionRange
, false)),
469 "defined(FLUZZY_FLOOF)");
471 const auto &Results7
=
472 DirectiveExprRange("#if 1\n#elif defined(FLOOFY)\n#endif\n");
473 EXPECT_EQ(Results7
.size(), 2U);
475 GetSourceStringToEnd(CharSourceRange(Results7
[0].ConditionRange
, false)),
478 GetSourceStringToEnd(CharSourceRange(Results7
[1].ConditionRange
, false)),
481 const auto &Results8
=
482 DirectiveExprRange("#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n");
483 EXPECT_EQ(Results8
.size(), 1U);
485 GetSourceStringToEnd(CharSourceRange(Results8
[0].ConditionRange
, false)),
486 "__FILE__ > FLOOFY");
488 Lexer::getSourceText(CharSourceRange(Results8
[0].ConditionRange
, false),
489 SourceMgr
, LangOpts
),
490 "__FILE__ > FLOOFY");