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)43   void makeModuleVisible(Module *Mod,
44                          Module::NameVisibilityKind Visibility,
45                          SourceLocation ImportLoc) override { }
46 
loadGlobalModuleIndex(SourceLocation TriggerLoc)47   GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override
48     { return nullptr; }
lookupMissingImports(StringRef Name,SourceLocation TriggerLoc)49   bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override
50     { return 0; }
51 };
52 
53 // Stub to collect data from InclusionDirective callbacks.
54 class InclusionDirectiveCallbacks : public PPCallbacks {
55 public:
InclusionDirective(SourceLocation HashLoc,const Token & IncludeTok,StringRef FileName,bool IsAngled,CharSourceRange FilenameRange,const FileEntry * File,StringRef SearchPath,StringRef RelativePath,const Module * Imported)56   void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
57                           StringRef FileName, bool IsAngled,
58                           CharSourceRange FilenameRange, const FileEntry *File,
59                           StringRef SearchPath, StringRef RelativePath,
60                           const Module *Imported) override {
61       this->HashLoc = HashLoc;
62       this->IncludeTok = IncludeTok;
63       this->FileName = FileName.str();
64       this->IsAngled = IsAngled;
65       this->FilenameRange = FilenameRange;
66       this->File = File;
67       this->SearchPath = SearchPath.str();
68       this->RelativePath = RelativePath.str();
69       this->Imported = Imported;
70   }
71 
72   SourceLocation HashLoc;
73   Token IncludeTok;
74   SmallString<16> FileName;
75   bool IsAngled;
76   CharSourceRange FilenameRange;
77   const FileEntry* File;
78   SmallString<16> SearchPath;
79   SmallString<16> RelativePath;
80   const Module* Imported;
81 };
82 
83 // Stub to collect data from PragmaOpenCLExtension callbacks.
84 class PragmaOpenCLExtensionCallbacks : public PPCallbacks {
85 public:
86   typedef struct {
87     SmallString<16> Name;
88     unsigned State;
89   } CallbackParameters;
90 
PragmaOpenCLExtensionCallbacks()91   PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {}
92 
PragmaOpenCLExtension(clang::SourceLocation NameLoc,const clang::IdentifierInfo * Name,clang::SourceLocation StateLoc,unsigned State)93   void PragmaOpenCLExtension(clang::SourceLocation NameLoc,
94                              const clang::IdentifierInfo *Name,
95                              clang::SourceLocation StateLoc,
96                              unsigned State) override {
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:
PPCallbacksTest()112   PPCallbacksTest()
113       : InMemoryFileSystem(new vfs::InMemoryFileSystem),
114         FileMgr(FileSystemOptions(), InMemoryFileSystem),
115         DiagID(new DiagnosticIDs()), 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   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem;
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       InMemoryFileSystem->addFile(HeaderPath, 0,
138                                   llvm::MemoryBuffer::getMemBuffer("\n"));
139 
140       // Add header's parent path to search path.
141       StringRef SearchPath = llvm::sys::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.
GetSourceString(CharSourceRange 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.
InclusionDirectiveFilenameRange(const char * SourceText,const char * HeaderPath,bool SystemHeader)157   CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText,
158       const char* HeaderPath, bool SystemHeader) {
159     std::unique_ptr<llvm::MemoryBuffer> Buf =
160         llvm::MemoryBuffer::getMemBuffer(SourceText);
161     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
162 
163     VoidModuleLoader ModLoader;
164 
165     IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts = new HeaderSearchOptions();
166     HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts,
167                             Target.get());
168     AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
169 
170     IntrusiveRefCntPtr<PreprocessorOptions> PPOpts = new PreprocessorOptions();
171     Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader,
172                     /*IILookup =*/nullptr,
173                     /*OwnsHeaderSearch =*/false);
174     PP.Initialize(*Target);
175     InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks;
176     PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
177 
178     // Lex source text.
179     PP.EnterMainSourceFile();
180 
181     while (true) {
182       Token Tok;
183       PP.Lex(Tok);
184       if (Tok.is(tok::eof))
185         break;
186     }
187 
188     // Callbacks have been executed at this point -- return filename range.
189     return Callbacks->FilenameRange;
190   }
191 
192   PragmaOpenCLExtensionCallbacks::CallbackParameters
PragmaOpenCLExtensionCall(const char * SourceText)193   PragmaOpenCLExtensionCall(const char* SourceText) {
194     LangOptions OpenCLLangOpts;
195     OpenCLLangOpts.OpenCL = 1;
196 
197     std::unique_ptr<llvm::MemoryBuffer> SourceBuf =
198         llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl");
199     SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
200 
201     VoidModuleLoader ModLoader;
202     HeaderSearch HeaderInfo(new HeaderSearchOptions, SourceMgr, Diags,
203                             OpenCLLangOpts, Target.get());
204 
205     Preprocessor PP(new PreprocessorOptions(), Diags, OpenCLLangOpts, SourceMgr,
206                     HeaderInfo, ModLoader, /*IILookup =*/nullptr,
207                     /*OwnsHeaderSearch =*/false);
208     PP.Initialize(*Target);
209 
210     // parser actually sets correct pragma handlers for preprocessor
211     // according to LangOptions, so we init Parser to register opencl
212     // pragma handlers
213     ASTContext Context(OpenCLLangOpts, SourceMgr,
214                        PP.getIdentifierTable(), PP.getSelectorTable(),
215                        PP.getBuiltinInfo());
216     Context.InitBuiltinTypes(*Target);
217 
218     ASTConsumer Consumer;
219     Sema S(PP, Context, Consumer);
220     Parser P(PP, S, false);
221     PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks;
222     PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
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 
TEST_F(PPCallbacksTest,QuotedFilename)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 
TEST_F(PPCallbacksTest,AngledFilename)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 
TEST_F(PPCallbacksTest,QuotedInMacro)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 
TEST_F(PPCallbacksTest,AngledInMacro)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 
TEST_F(PPCallbacksTest,StringizedMacroArgument)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 
TEST_F(PPCallbacksTest,ConcatenatedMacroArgument)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 
TEST_F(PPCallbacksTest,TrigraphFilename)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 
TEST_F(PPCallbacksTest,TrigraphInMacro)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 
TEST_F(PPCallbacksTest,OpenCLExtensionPragmaEnabled)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 
TEST_F(PPCallbacksTest,OpenCLExtensionPragmaDisabled)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