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,
46                         bool Complain) 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.
55class InclusionDirectiveCallbacks : public PPCallbacks {
56public:
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.
85class PragmaOpenCLExtensionCallbacks : public PPCallbacks {
86public:
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.
111class PPCallbacksTest : public ::testing::Test {
112protected:
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.
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.
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.
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
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
240TEST_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
250TEST_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
260TEST_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
271TEST_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
282TEST_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
293TEST_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
305TEST_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
315TEST_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
326TEST_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
338TEST_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