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