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