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