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