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
30using namespace clang;
31
32namespace {
33
34// Stub out module loading.
35class VoidModuleLoader : public ModuleLoader {
36  ModuleLoadResult loadModule(SourceLocation ImportLoc,
37                              ModuleIdPath Path,
38                              Module::NameVisibilityKind Visibility,
39                              bool IsInclusionDirective) override {
40    return ModuleLoadResult();
41  }
42
43  void makeModuleVisible(Module *Mod,
44                         Module::NameVisibilityKind Visibility,
45                         SourceLocation ImportLoc) override { }
46
47  GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override
48    { return nullptr; }
49  bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override
50    { return 0; }
51};
52
53// Stub to collect data from InclusionDirective callbacks.
54class InclusionDirectiveCallbacks : public PPCallbacks {
55public:
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.
84class PragmaOpenCLExtensionCallbacks : public PPCallbacks {
85public:
86  typedef struct {
87    SmallString<16> Name;
88    unsigned State;
89  } CallbackParameters;
90
91  PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {}
92
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.
110class PPCallbacksTest : public ::testing::Test {
111protected:
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.
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.
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.
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
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
241TEST_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
251TEST_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
261TEST_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
272TEST_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
283TEST_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
294TEST_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
306TEST_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
316TEST_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
327TEST_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
339TEST_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