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