1 //===- unittests/Lex/PPCallbacksTest.cpp - PPCallbacks tests ------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===--------------------------------------------------------------===//
9
10 #include "clang/Lex/Preprocessor.h"
11 #include "clang/AST/ASTConsumer.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/Basic/Diagnostic.h"
14 #include "clang/Basic/DiagnosticOptions.h"
15 #include "clang/Basic/FileManager.h"
16 #include "clang/Basic/LangOptions.h"
17 #include "clang/Basic/SourceManager.h"
18 #include "clang/Basic/TargetInfo.h"
19 #include "clang/Basic/TargetOptions.h"
20 #include "clang/Lex/HeaderSearch.h"
21 #include "clang/Lex/HeaderSearchOptions.h"
22 #include "clang/Lex/ModuleLoader.h"
23 #include "clang/Lex/PreprocessorOptions.h"
24 #include "clang/Parse/Parser.h"
25 #include "clang/Sema/Sema.h"
26 #include "llvm/ADT/SmallString.h"
27 #include "llvm/Support/Path.h"
28 #include "gtest/gtest.h"
29
30 using namespace clang;
31
32 namespace {
33
34 // Stub out module loading.
35 class VoidModuleLoader : public ModuleLoader {
loadModule(SourceLocation ImportLoc,ModuleIdPath Path,Module::NameVisibilityKind Visibility,bool IsInclusionDirective)36 ModuleLoadResult loadModule(SourceLocation ImportLoc,
37 ModuleIdPath Path,
38 Module::NameVisibilityKind Visibility,
39 bool IsInclusionDirective) override {
40 return ModuleLoadResult();
41 }
42
makeModuleVisible(Module * Mod,Module::NameVisibilityKind Visibility,SourceLocation ImportLoc,bool Complain)43 void makeModuleVisible(Module *Mod,
44 Module::NameVisibilityKind Visibility,
45 SourceLocation ImportLoc,
46 bool Complain) override { }
47
loadGlobalModuleIndex(SourceLocation TriggerLoc)48 GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override
49 { return nullptr; }
lookupMissingImports(StringRef Name,SourceLocation TriggerLoc)50 bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override
51 { return 0; };
52 };
53
54 // Stub to collect data from InclusionDirective callbacks.
55 class InclusionDirectiveCallbacks : public PPCallbacks {
56 public:
InclusionDirective(SourceLocation HashLoc,const Token & IncludeTok,StringRef FileName,bool IsAngled,CharSourceRange FilenameRange,const FileEntry * File,StringRef SearchPath,StringRef RelativePath,const Module * Imported)57 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
58 StringRef FileName, bool IsAngled,
59 CharSourceRange FilenameRange, const FileEntry *File,
60 StringRef SearchPath, StringRef RelativePath,
61 const Module *Imported) override {
62 this->HashLoc = HashLoc;
63 this->IncludeTok = IncludeTok;
64 this->FileName = FileName.str();
65 this->IsAngled = IsAngled;
66 this->FilenameRange = FilenameRange;
67 this->File = File;
68 this->SearchPath = SearchPath.str();
69 this->RelativePath = RelativePath.str();
70 this->Imported = Imported;
71 }
72
73 SourceLocation HashLoc;
74 Token IncludeTok;
75 SmallString<16> FileName;
76 bool IsAngled;
77 CharSourceRange FilenameRange;
78 const FileEntry* File;
79 SmallString<16> SearchPath;
80 SmallString<16> RelativePath;
81 const Module* Imported;
82 };
83
84 // Stub to collect data from PragmaOpenCLExtension callbacks.
85 class PragmaOpenCLExtensionCallbacks : public PPCallbacks {
86 public:
87 typedef struct {
88 SmallString<16> Name;
89 unsigned State;
90 } CallbackParameters;
91
PragmaOpenCLExtensionCallbacks()92 PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {};
93
PragmaOpenCLExtension(clang::SourceLocation NameLoc,const clang::IdentifierInfo * Name,clang::SourceLocation StateLoc,unsigned State)94 void PragmaOpenCLExtension(clang::SourceLocation NameLoc,
95 const clang::IdentifierInfo *Name,
96 clang::SourceLocation StateLoc,
97 unsigned State) override {
98 this->NameLoc = NameLoc;
99 this->Name = Name->getName();
100 this->StateLoc = StateLoc;
101 this->State = State;
102 };
103
104 SourceLocation NameLoc;
105 SmallString<16> Name;
106 SourceLocation StateLoc;
107 unsigned State;
108 };
109
110 // PPCallbacks test fixture.
111 class PPCallbacksTest : public ::testing::Test {
112 protected:
PPCallbacksTest()113 PPCallbacksTest()
114 : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
115 DiagOpts(new DiagnosticOptions()),
116 Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()),
117 SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) {
118 TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
119 Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
120 }
121
122 FileSystemOptions FileMgrOpts;
123 FileManager FileMgr;
124 IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
125 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
126 DiagnosticsEngine Diags;
127 SourceManager SourceMgr;
128 LangOptions LangOpts;
129 std::shared_ptr<TargetOptions> TargetOpts;
130 IntrusiveRefCntPtr<TargetInfo> Target;
131
132 // Register a header path as a known file and add its location
133 // to search path.
AddFakeHeader(HeaderSearch & HeaderInfo,const char * HeaderPath,bool IsSystemHeader)134 void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath,
135 bool IsSystemHeader) {
136 // Tell FileMgr about header.
137 FileMgr.getVirtualFile(HeaderPath, 0, 0);
138
139 // Add header's parent path to search path.
140 StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath);
141 const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath);
142 DirectoryLookup DL(DE, SrcMgr::C_User, false);
143 HeaderInfo.AddSearchPath(DL, IsSystemHeader);
144 }
145
146 // Get the raw source string of the range.
GetSourceString(CharSourceRange Range)147 StringRef GetSourceString(CharSourceRange Range) {
148 const char* B = SourceMgr.getCharacterData(Range.getBegin());
149 const char* E = SourceMgr.getCharacterData(Range.getEnd());
150
151 return StringRef(B, E - B);
152 }
153
154 // Run lexer over SourceText and collect FilenameRange from
155 // the InclusionDirective callback.
InclusionDirectiveFilenameRange(const char * SourceText,const char * HeaderPath,bool SystemHeader)156 CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText,
157 const char* HeaderPath, bool SystemHeader) {
158 std::unique_ptr<llvm::MemoryBuffer> Buf =
159 llvm::MemoryBuffer::getMemBuffer(SourceText);
160 SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
161
162 VoidModuleLoader ModLoader;
163
164 IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts = new HeaderSearchOptions();
165 HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts,
166 Target.get());
167 AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
168
169 IntrusiveRefCntPtr<PreprocessorOptions> PPOpts = new PreprocessorOptions();
170 Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader,
171 /*IILookup =*/nullptr,
172 /*OwnsHeaderSearch =*/false);
173 PP.Initialize(*Target);
174 InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks;
175 PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
176
177 // Lex source text.
178 PP.EnterMainSourceFile();
179
180 while (true) {
181 Token Tok;
182 PP.Lex(Tok);
183 if (Tok.is(tok::eof))
184 break;
185 }
186
187 // Callbacks have been executed at this point -- return filename range.
188 return Callbacks->FilenameRange;
189 }
190
191 PragmaOpenCLExtensionCallbacks::CallbackParameters
PragmaOpenCLExtensionCall(const char * SourceText)192 PragmaOpenCLExtensionCall(const char* SourceText) {
193 LangOptions OpenCLLangOpts;
194 OpenCLLangOpts.OpenCL = 1;
195
196 std::unique_ptr<llvm::MemoryBuffer> SourceBuf =
197 llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl");
198 SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
199
200 VoidModuleLoader ModLoader;
201 HeaderSearch HeaderInfo(new HeaderSearchOptions, SourceMgr, Diags,
202 OpenCLLangOpts, Target.get());
203
204 Preprocessor PP(new PreprocessorOptions(), Diags, OpenCLLangOpts, SourceMgr,
205 HeaderInfo, ModLoader, /*IILookup =*/nullptr,
206 /*OwnsHeaderSearch =*/false);
207 PP.Initialize(*Target);
208
209 // parser actually sets correct pragma handlers for preprocessor
210 // according to LangOptions, so we init Parser to register opencl
211 // pragma handlers
212 ASTContext Context(OpenCLLangOpts, SourceMgr,
213 PP.getIdentifierTable(), PP.getSelectorTable(),
214 PP.getBuiltinInfo());
215 Context.InitBuiltinTypes(*Target);
216
217 ASTConsumer Consumer;
218 Sema S(PP, Context, Consumer);
219 Parser P(PP, S, false);
220 PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks;
221 PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
222
223 // Lex source text.
224 PP.EnterMainSourceFile();
225 while (true) {
226 Token Tok;
227 PP.Lex(Tok);
228 if (Tok.is(tok::eof))
229 break;
230 }
231
232 PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = {
233 Callbacks->Name,
234 Callbacks->State
235 };
236 return RetVal;
237 }
238 };
239
TEST_F(PPCallbacksTest,QuotedFilename)240 TEST_F(PPCallbacksTest, QuotedFilename) {
241 const char* Source =
242 "#include \"quoted.h\"\n";
243
244 CharSourceRange Range =
245 InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
246
247 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
248 }
249
TEST_F(PPCallbacksTest,AngledFilename)250 TEST_F(PPCallbacksTest, AngledFilename) {
251 const char* Source =
252 "#include <angled.h>\n";
253
254 CharSourceRange Range =
255 InclusionDirectiveFilenameRange(Source, "/angled.h", true);
256
257 ASSERT_EQ("<angled.h>", GetSourceString(Range));
258 }
259
TEST_F(PPCallbacksTest,QuotedInMacro)260 TEST_F(PPCallbacksTest, QuotedInMacro) {
261 const char* Source =
262 "#define MACRO_QUOTED \"quoted.h\"\n"
263 "#include MACRO_QUOTED\n";
264
265 CharSourceRange Range =
266 InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
267
268 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
269 }
270
TEST_F(PPCallbacksTest,AngledInMacro)271 TEST_F(PPCallbacksTest, AngledInMacro) {
272 const char* Source =
273 "#define MACRO_ANGLED <angled.h>\n"
274 "#include MACRO_ANGLED\n";
275
276 CharSourceRange Range =
277 InclusionDirectiveFilenameRange(Source, "/angled.h", true);
278
279 ASSERT_EQ("<angled.h>", GetSourceString(Range));
280 }
281
TEST_F(PPCallbacksTest,StringizedMacroArgument)282 TEST_F(PPCallbacksTest, StringizedMacroArgument) {
283 const char* Source =
284 "#define MACRO_STRINGIZED(x) #x\n"
285 "#include MACRO_STRINGIZED(quoted.h)\n";
286
287 CharSourceRange Range =
288 InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
289
290 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
291 }
292
TEST_F(PPCallbacksTest,ConcatenatedMacroArgument)293 TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) {
294 const char* Source =
295 "#define MACRO_ANGLED <angled.h>\n"
296 "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
297 "#include MACRO_CONCAT(MACRO, ANGLED)\n";
298
299 CharSourceRange Range =
300 InclusionDirectiveFilenameRange(Source, "/angled.h", false);
301
302 ASSERT_EQ("<angled.h>", GetSourceString(Range));
303 }
304
TEST_F(PPCallbacksTest,TrigraphFilename)305 TEST_F(PPCallbacksTest, TrigraphFilename) {
306 const char* Source =
307 "#include \"tri\?\?-graph.h\"\n";
308
309 CharSourceRange Range =
310 InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
311
312 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
313 }
314
TEST_F(PPCallbacksTest,TrigraphInMacro)315 TEST_F(PPCallbacksTest, TrigraphInMacro) {
316 const char* Source =
317 "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
318 "#include MACRO_TRIGRAPH\n";
319
320 CharSourceRange Range =
321 InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
322
323 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
324 }
325
TEST_F(PPCallbacksTest,OpenCLExtensionPragmaEnabled)326 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) {
327 const char* Source =
328 "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n";
329
330 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
331 PragmaOpenCLExtensionCall(Source);
332
333 ASSERT_EQ("cl_khr_fp64", Parameters.Name);
334 unsigned ExpectedState = 1;
335 ASSERT_EQ(ExpectedState, Parameters.State);
336 }
337
TEST_F(PPCallbacksTest,OpenCLExtensionPragmaDisabled)338 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) {
339 const char* Source =
340 "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n";
341
342 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
343 PragmaOpenCLExtensionCall(Source);
344
345 ASSERT_EQ("cl_khr_fp16", Parameters.Name);
346 unsigned ExpectedState = 0;
347 ASSERT_EQ(ExpectedState, Parameters.State);
348 }
349
350 } // anonoymous namespace
351