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