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