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/Basic/Diagnostic.h"
12 #include "clang/Basic/FileManager.h"
13 #include "clang/Basic/LangOptions.h"
14 #include "clang/Basic/SourceManager.h"
15 #include "clang/Basic/TargetInfo.h"
16 #include "clang/Basic/TargetOptions.h"
17 #include "clang/Lex/HeaderSearch.h"
18 #include "clang/Lex/HeaderSearchOptions.h"
19 #include "clang/Lex/ModuleLoader.h"
20 #include "clang/Lex/PreprocessorOptions.h"
21 #include "clang/Parse/Parser.h"
22 #include "clang/Sema/Sema.h"
23 #include "clang/AST/ASTContext.h"
24 #include "clang/AST/ASTConsumer.h"
25 #include "llvm/ADT/SmallString.h"
26 #include "llvm/Support/Path.h"
27 #include "gtest/gtest.h"
28 
29 using namespace llvm;
30 using namespace llvm::sys;
31 using namespace clang;
32 
33 namespace {
34 
35 // Stub out module loading.
36 class VoidModuleLoader : public ModuleLoader {
37   virtual ModuleLoadResult loadModule(SourceLocation ImportLoc,
38                                       ModuleIdPath Path,
39                                       Module::NameVisibilityKind Visibility,
40                                       bool IsInclusionDirective) {
41     return ModuleLoadResult();
42   }
43 
44   virtual void makeModuleVisible(Module *Mod,
45                                  Module::NameVisibilityKind Visibility,
46                                  SourceLocation ImportLoc,
47                                  bool Complain) { }
48 };
49 
50 // Stub to collect data from InclusionDirective callbacks.
51 class InclusionDirectiveCallbacks : public PPCallbacks {
52 public:
53   void InclusionDirective(SourceLocation HashLoc,
54     const Token &IncludeTok,
55     StringRef FileName,
56     bool IsAngled,
57     CharSourceRange FilenameRange,
58     const FileEntry *File,
59     StringRef SearchPath,
60     StringRef RelativePath,
61     const Module *Imported) {
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 
92   PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {};
93 
94   void PragmaOpenCLExtension(
95     clang::SourceLocation NameLoc, const clang::IdentifierInfo *Name,
96     clang::SourceLocation StateLoc, unsigned State) {
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:
112   PPCallbacksTest()
113     : FileMgr(FileMgrOpts),
114       DiagID(new DiagnosticIDs()),
115       DiagOpts(new DiagnosticOptions()),
116       Diags(DiagID, DiagOpts.getPtr(), new IgnoringDiagConsumer()),
117       SourceMgr(Diags, FileMgr) {
118     TargetOpts = new TargetOptions();
119     TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
120     Target = TargetInfo::CreateTargetInfo(Diags, &*TargetOpts);
121   }
122 
123   FileSystemOptions FileMgrOpts;
124   FileManager FileMgr;
125   IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
126   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
127   DiagnosticsEngine Diags;
128   SourceManager SourceMgr;
129   LangOptions LangOpts;
130   IntrusiveRefCntPtr<TargetOptions> TargetOpts;
131   IntrusiveRefCntPtr<TargetInfo> Target;
132 
133   // Register a header path as a known file and add its location
134   // to search path.
135   void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath,
136     bool IsSystemHeader) {
137       // Tell FileMgr about header.
138       FileMgr.getVirtualFile(HeaderPath, 0, 0);
139 
140       // Add header's parent path to search path.
141       StringRef SearchPath = 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.
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.
157   CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText,
158       const char* HeaderPath, bool SystemHeader) {
159     MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(SourceText);
160     (void)SourceMgr.createMainFileIDForMemBuffer(Buf);
161 
162     VoidModuleLoader ModLoader;
163 
164     IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts = new HeaderSearchOptions();
165     HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts,
166                             Target.getPtr());
167     AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
168 
169     IntrusiveRefCntPtr<PreprocessorOptions> PPOpts = new PreprocessorOptions();
170     Preprocessor PP(PPOpts, Diags, LangOpts,
171       Target.getPtr(),
172       SourceMgr, HeaderInfo, ModLoader,
173       /*IILookup =*/ 0,
174       /*OwnsHeaderSearch =*/false,
175       /*DelayInitialization =*/ false);
176     InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks;
177     PP.addPPCallbacks(Callbacks); // Takes ownership.
178 
179     // Lex source text.
180     PP.EnterMainSourceFile();
181 
182     while (true) {
183       Token Tok;
184       PP.Lex(Tok);
185       if (Tok.is(tok::eof))
186         break;
187     }
188 
189     // Callbacks have been executed at this point -- return filename range.
190     return Callbacks->FilenameRange;
191   }
192 
193   PragmaOpenCLExtensionCallbacks::CallbackParameters
194   PragmaOpenCLExtensionCall(const char* SourceText) {
195     LangOptions OpenCLLangOpts;
196     OpenCLLangOpts.OpenCL = 1;
197 
198     MemoryBuffer* sourceBuf = MemoryBuffer::getMemBuffer(SourceText, "test.cl");
199     (void)SourceMgr.createMainFileIDForMemBuffer(sourceBuf);
200 
201     VoidModuleLoader ModLoader;
202     HeaderSearch HeaderInfo(new HeaderSearchOptions, SourceMgr, Diags,
203                             OpenCLLangOpts, Target.getPtr());
204 
205     Preprocessor PP(new PreprocessorOptions(), Diags, OpenCLLangOpts,
206                     Target.getPtr(),
207                     SourceMgr, HeaderInfo, ModLoader,
208                    /*IILookup =*/ 0,
209                     /*OwnsHeaderSearch =*/false,
210                     /*DelayInitialization =*/ false);
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, Target.getPtr(),
216                        PP.getIdentifierTable(), PP.getSelectorTable(),
217                        PP.getBuiltinInfo(), 0);
218     ASTConsumer Consumer;
219     Sema S(PP, Context, Consumer);
220     Parser P(PP, S, false);
221     PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks;
222     PP.addPPCallbacks(Callbacks); // Takes ownership.
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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