Clang Project

clang_source_code/unittests/Lex/PPCallbacksTest.cpp
1//===- unittests/Lex/PPCallbacksTest.cpp - PPCallbacks tests ------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===--------------------------------------------------------------===//
8
9#include "clang/Lex/Preprocessor.h"
10#include "clang/AST/ASTConsumer.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/Basic/Diagnostic.h"
13#include "clang/Basic/DiagnosticOptions.h"
14#include "clang/Basic/FileManager.h"
15#include "clang/Basic/LangOptions.h"
16#include "clang/Basic/SourceManager.h"
17#include "clang/Basic/TargetInfo.h"
18#include "clang/Basic/TargetOptions.h"
19#include "clang/Lex/HeaderSearch.h"
20#include "clang/Lex/HeaderSearchOptions.h"
21#include "clang/Lex/ModuleLoader.h"
22#include "clang/Lex/PreprocessorOptions.h"
23#include "clang/Parse/Parser.h"
24#include "clang/Sema/Sema.h"
25#include "llvm/ADT/SmallString.h"
26#include "llvm/Support/Path.h"
27#include "gtest/gtest.h"
28
29using namespace clang;
30
31namespace {
32
33// Stub to collect data from InclusionDirective callbacks.
34class InclusionDirectiveCallbacks : public PPCallbacks {
35public:
36  void InclusionDirective(SourceLocation HashLocconst Token &IncludeTok,
37                          StringRef FileNamebool IsAngled,
38                          CharSourceRange FilenameRangeconst FileEntry *File,
39                          StringRef SearchPathStringRef RelativePath,
40                          const Module *Imported,
41                          SrcMgr::CharacteristicKind FileType) override {
42    this->HashLoc = HashLoc;
43    this->IncludeTok = IncludeTok;
44    this->FileName = FileName.str();
45    this->IsAngled = IsAngled;
46    this->FilenameRange = FilenameRange;
47    this->File = File;
48    this->SearchPath = SearchPath.str();
49    this->RelativePath = RelativePath.str();
50    this->Imported = Imported;
51    this->FileType = FileType;
52  }
53
54  SourceLocation HashLoc;
55  Token IncludeTok;
56  SmallString<16FileName;
57  bool IsAngled;
58  CharSourceRange FilenameRange;
59  const FileEntryFile;
60  SmallString<16SearchPath;
61  SmallString<16RelativePath;
62  const ModuleImported;
63  SrcMgr::CharacteristicKind FileType;
64};
65
66class CondDirectiveCallbacks : public PPCallbacks {
67public:
68  struct Result {
69    SourceRange ConditionRange;
70    ConditionValueKind ConditionValue;
71
72    Result(SourceRange RConditionValueKind K)
73        : ConditionRange(R), ConditionValue(K) {}
74  };
75
76  std::vector<ResultResults;
77
78  void If(SourceLocation LocSourceRange ConditionRange,
79          ConditionValueKind ConditionValue) override {
80    Results.emplace_back(ConditionRangeConditionValue);
81  }
82
83  void Elif(SourceLocation LocSourceRange ConditionRange,
84            ConditionValueKind ConditionValueSourceLocation IfLoc) override {
85    Results.emplace_back(ConditionRangeConditionValue);
86  }
87};
88
89// Stub to collect data from PragmaOpenCLExtension callbacks.
90class PragmaOpenCLExtensionCallbacks : public PPCallbacks {
91public:
92  typedef struct {
93    SmallString<16Name;
94    unsigned State;
95  } CallbackParameters;
96
97  PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {}
98
99  void PragmaOpenCLExtension(clang::SourceLocation NameLoc,
100                             const clang::IdentifierInfo *Name,
101                             clang::SourceLocation StateLoc,
102                             unsigned State) override {
103      this->NameLoc = NameLoc;
104      this->Name = Name->getName();
105      this->StateLoc = StateLoc;
106      this->State = State;
107  }
108
109  SourceLocation NameLoc;
110  SmallString<16Name;
111  SourceLocation StateLoc;
112  unsigned State;
113};
114
115// PPCallbacks test fixture.
116class PPCallbacksTest : public ::testing::Test {
117protected:
118  PPCallbacksTest()
119      : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
120        FileMgr(FileSystemOptions(), InMemoryFileSystem),
121        DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()),
122        Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()),
123        SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) {
124    TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
125    Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
126  }
127
128  IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
129  FileManager FileMgr;
130  IntrusiveRefCntPtr<DiagnosticIDsDiagID;
131  IntrusiveRefCntPtr<DiagnosticOptionsDiagOpts;
132  DiagnosticsEngine Diags;
133  SourceManager SourceMgr;
134  LangOptions LangOpts;
135  std::shared_ptr<TargetOptionsTargetOpts;
136  IntrusiveRefCntPtr<TargetInfoTarget;
137
138  // Register a header path as a known file and add its location
139  // to search path.
140  void AddFakeHeader(HeaderSearch &HeaderInfoconst char *HeaderPath,
141                     bool IsSystemHeader) {
142    // Tell FileMgr about header.
143    InMemoryFileSystem->addFile(HeaderPath, 0,
144                                llvm::MemoryBuffer::getMemBuffer("\n"));
145
146    // Add header's parent path to search path.
147    StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath);
148    const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath);
149    DirectoryLookup DL(DESrcMgr::C_Userfalse);
150    HeaderInfo.AddSearchPath(DLIsSystemHeader);
151  }
152
153  // Get the raw source string of the range.
154  StringRef GetSourceString(CharSourceRange Range) {
155    const charB = SourceMgr.getCharacterData(Range.getBegin());
156    const charE = SourceMgr.getCharacterData(Range.getEnd());
157
158    return StringRef(B, E - B);
159  }
160
161  StringRef GetSourceStringToEnd(CharSourceRange Range) {
162    const char *B = SourceMgr.getCharacterData(Range.getBegin());
163    const char *E = SourceMgr.getCharacterData(Range.getEnd());
164
165    return StringRef(
166        B,
167        E - B + Lexer::MeasureTokenLength(Range.getEnd(), SourceMgr, LangOpts));
168  }
169
170  // Run lexer over SourceText and collect FilenameRange from
171  // the InclusionDirective callback.
172  CharSourceRange InclusionDirectiveFilenameRange(const char *SourceText,
173                                                  const char *HeaderPath,
174                                                  bool SystemHeader) {
175    std::unique_ptr<llvm::MemoryBufferBuf =
176        llvm::MemoryBuffer::getMemBuffer(SourceText);
177    SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
178
179    TrivialModuleLoader ModLoader;
180
181    HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
182                            Diags, LangOpts, Target.get());
183    AddFakeHeader(HeaderInfoHeaderPathSystemHeader);
184
185    Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
186                    SourceMgr, HeaderInfo, ModLoader,
187                    /*IILookup =*/nullptr,
188                    /*OwnsHeaderSearch =*/false);
189    return InclusionDirectiveCallback(PP)->FilenameRange;
190  }
191
192  SrcMgr::CharacteristicKind InclusionDirectiveCharacteristicKind(
193      const char *SourceTextconst char *HeaderPathbool SystemHeader) {
194    std::unique_ptr<llvm::MemoryBufferBuf =
195        llvm::MemoryBuffer::getMemBuffer(SourceText);
196    SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
197
198    TrivialModuleLoader ModLoader;
199
200    HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
201                            Diags, LangOpts, Target.get());
202    AddFakeHeader(HeaderInfoHeaderPathSystemHeader);
203
204    Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
205                    SourceMgr, HeaderInfo, ModLoader,
206                    /*IILookup =*/nullptr,
207                    /*OwnsHeaderSearch =*/false);
208    return InclusionDirectiveCallback(PP)->FileType;
209  }
210
211  InclusionDirectiveCallbacks *InclusionDirectiveCallback(Preprocessor &PP) {
212    PP.Initialize(*Target);
213    InclusionDirectiveCallbacksCallbacks = new InclusionDirectiveCallbacks;
214    PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
215
216    // Lex source text.
217    PP.EnterMainSourceFile();
218
219    while (true) {
220      Token Tok;
221      PP.Lex(Tok);
222      if (Tok.is(tok::eof))
223        break;
224    }
225
226    // Callbacks have been executed at this point -- return filename range.
227    return Callbacks;
228  }
229
230  std::vector<CondDirectiveCallbacks::Result>
231  DirectiveExprRange(StringRef SourceText) {
232    TrivialModuleLoader ModLoader;
233    std::unique_ptr<llvm::MemoryBufferBuf =
234        llvm::MemoryBuffer::getMemBuffer(SourceText);
235    SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
236    HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
237                            Diags, LangOpts, Target.get());
238    Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
239                    SourceMgr, HeaderInfo, ModLoader,
240                    /*IILookup =*/nullptr,
241                    /*OwnsHeaderSearch =*/false);
242    PP.Initialize(*Target);
243    auto *Callbacks = new CondDirectiveCallbacks;
244    PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
245
246    // Lex source text.
247    PP.EnterMainSourceFile();
248
249    while (true) {
250      Token Tok;
251      PP.Lex(Tok);
252      if (Tok.is(tok::eof))
253        break;
254    }
255
256    return Callbacks->Results;
257  }
258
259  PragmaOpenCLExtensionCallbacks::CallbackParameters
260  PragmaOpenCLExtensionCall(const char *SourceText) {
261    LangOptions OpenCLLangOpts;
262    OpenCLLangOpts.OpenCL = 1;
263
264    std::unique_ptr<llvm::MemoryBufferSourceBuf =
265        llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl");
266    SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
267
268    TrivialModuleLoader ModLoader;
269    HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
270                            Diags, OpenCLLangOpts, Target.get());
271
272    Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags,
273                    OpenCLLangOpts, SourceMgr, HeaderInfo, ModLoader,
274                    /*IILookup =*/nullptr,
275                    /*OwnsHeaderSearch =*/false);
276    PP.Initialize(*Target);
277
278    // parser actually sets correct pragma handlers for preprocessor
279    // according to LangOptions, so we init Parser to register opencl
280    // pragma handlers
281    ASTContext Context(OpenCLLangOpts, SourceMgr, PP.getIdentifierTable(),
282                       PP.getSelectorTable(), PP.getBuiltinInfo());
283    Context.InitBuiltinTypes(*Target);
284
285    ASTConsumer Consumer;
286    Sema S(PPContextConsumer);
287    Parser P(PPSfalse);
288    PragmaOpenCLExtensionCallbacksCallbacks = new PragmaOpenCLExtensionCallbacks;
289    PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
290
291    // Lex source text.
292    PP.EnterMainSourceFile();
293    while (true) {
294      Token Tok;
295      PP.Lex(Tok);
296      if (Tok.is(tok::eof))
297        break;
298    }
299
300    PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = {
301      Callbacks->Name,
302      Callbacks->State
303    };
304    return RetVal;
305  }
306};
307
308TEST_F(PPCallbacksTest, UserFileCharacteristics) {
309  const char *Source = "#include \"quoted.h\"\n";
310
311  SrcMgr::CharacteristicKind Kind =
312      InclusionDirectiveCharacteristicKind(Source"/quoted.h"false);
313
314  ASSERT_EQ(SrcMgr::CharacteristicKind::C_UserKind);
315}
316
317TEST_F(PPCallbacksTest, QuotedFilename) {
318  const charSource =
319    "#include \"quoted.h\"\n";
320
321  CharSourceRange Range =
322    InclusionDirectiveFilenameRange(Source"/quoted.h"false);
323
324  ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
325}
326
327TEST_F(PPCallbacksTest, AngledFilename) {
328  const charSource =
329    "#include <angled.h>\n";
330
331  CharSourceRange Range =
332    InclusionDirectiveFilenameRange(Source"/angled.h"true);
333
334  ASSERT_EQ("<angled.h>", GetSourceString(Range));
335}
336
337TEST_F(PPCallbacksTest, QuotedInMacro) {
338  const charSource =
339    "#define MACRO_QUOTED \"quoted.h\"\n"
340    "#include MACRO_QUOTED\n";
341
342  CharSourceRange Range =
343    InclusionDirectiveFilenameRange(Source"/quoted.h"false);
344
345  ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
346}
347
348TEST_F(PPCallbacksTest, AngledInMacro) {
349  const charSource =
350    "#define MACRO_ANGLED <angled.h>\n"
351    "#include MACRO_ANGLED\n";
352
353  CharSourceRange Range =
354    InclusionDirectiveFilenameRange(Source"/angled.h"true);
355
356  ASSERT_EQ("<angled.h>", GetSourceString(Range));
357}
358
359TEST_F(PPCallbacksTest, StringizedMacroArgument) {
360  const charSource =
361    "#define MACRO_STRINGIZED(x) #x\n"
362    "#include MACRO_STRINGIZED(quoted.h)\n";
363
364  CharSourceRange Range =
365    InclusionDirectiveFilenameRange(Source"/quoted.h"false);
366
367  ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
368}
369
370TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) {
371  const charSource =
372    "#define MACRO_ANGLED <angled.h>\n"
373    "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
374    "#include MACRO_CONCAT(MACRO, ANGLED)\n";
375
376  CharSourceRange Range =
377    InclusionDirectiveFilenameRange(Source"/angled.h"false);
378
379  ASSERT_EQ("<angled.h>", GetSourceString(Range));
380}
381
382TEST_F(PPCallbacksTest, TrigraphFilename) {
383  const charSource =
384    "#include \"tri\?\?-graph.h\"\n";
385
386  CharSourceRange Range =
387    InclusionDirectiveFilenameRange(Source"/tri~graph.h"false);
388
389  ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
390}
391
392TEST_F(PPCallbacksTest, TrigraphInMacro) {
393  const charSource =
394    "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
395    "#include MACRO_TRIGRAPH\n";
396
397  CharSourceRange Range =
398    InclusionDirectiveFilenameRange(Source"/tri~graph.h"false);
399
400  ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
401}
402
403TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) {
404  const charSource =
405    "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n";
406
407  PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
408    PragmaOpenCLExtensionCall(Source);
409
410  ASSERT_EQ("cl_khr_fp64"Parameters.Name);
411  unsigned ExpectedState = 1;
412  ASSERT_EQ(ExpectedStateParameters.State);
413}
414
415TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) {
416  const charSource =
417    "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n";
418
419  PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters =
420    PragmaOpenCLExtensionCall(Source);
421
422  ASSERT_EQ("cl_khr_fp16"Parameters.Name);
423  unsigned ExpectedState = 0;
424  ASSERT_EQ(ExpectedStateParameters.State);
425}
426
427TEST_F(PPCallbacksTest, DirectiveExprRanges) {
428  const auto &Results1 = DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n");
429  EXPECT_EQ(Results1.size(), 1U);
430  EXPECT_EQ(
431      GetSourceStringToEnd(CharSourceRange(Results1[0].ConditionRange, false)),
432      "FLUZZY_FLOOF");
433
434  const auto &Results2 = DirectiveExprRange("#if 1 + 4 < 7\n#endif\n");
435  EXPECT_EQ(Results2.size(), 1U);
436  EXPECT_EQ(
437      GetSourceStringToEnd(CharSourceRange(Results2[0].ConditionRange, false)),
438      "1 + 4 < 7");
439
440  const auto &Results3 = DirectiveExprRange("#if 1 + \\\n  2\n#endif\n");
441  EXPECT_EQ(Results3.size(), 1U);
442  EXPECT_EQ(
443      GetSourceStringToEnd(CharSourceRange(Results3[0].ConditionRange, false)),
444      "1 + \\\n  2");
445
446  const auto &Results4 = DirectiveExprRange("#if 0\n#elif FLOOFY\n#endif\n");
447  EXPECT_EQ(Results4.size(), 2U);
448  EXPECT_EQ(
449      GetSourceStringToEnd(CharSourceRange(Results4[0].ConditionRange, false)),
450      "0");
451  EXPECT_EQ(
452      GetSourceStringToEnd(CharSourceRange(Results4[1].ConditionRange, false)),
453      "FLOOFY");
454
455  const auto &Results5 = DirectiveExprRange("#if 1\n#elif FLOOFY\n#endif\n");
456  EXPECT_EQ(Results5.size(), 2U);
457  EXPECT_EQ(
458      GetSourceStringToEnd(CharSourceRange(Results5[0].ConditionRange, false)),
459      "1");
460  EXPECT_EQ(
461      GetSourceStringToEnd(CharSourceRange(Results5[1].ConditionRange, false)),
462      "FLOOFY");
463
464  const auto &Results6 =
465      DirectiveExprRange("#if defined(FLUZZY_FLOOF)\n#endif\n");
466  EXPECT_EQ(Results6.size(), 1U);
467  EXPECT_EQ(
468      GetSourceStringToEnd(CharSourceRange(Results6[0].ConditionRange, false)),
469      "defined(FLUZZY_FLOOF)");
470
471  const auto &Results7 =
472      DirectiveExprRange("#if 1\n#elif defined(FLOOFY)\n#endif\n");
473  EXPECT_EQ(Results7.size(), 2U);
474  EXPECT_EQ(
475      GetSourceStringToEnd(CharSourceRange(Results7[0].ConditionRange, false)),
476      "1");
477  EXPECT_EQ(
478      GetSourceStringToEnd(CharSourceRange(Results7[1].ConditionRange, false)),
479      "defined(FLOOFY)");
480
481  const auto &Results8 =
482      DirectiveExprRange("#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n");
483  EXPECT_EQ(Results8.size(), 1U);
484  EXPECT_EQ(
485      GetSourceStringToEnd(CharSourceRange(Results8[0].ConditionRange, false)),
486      "__FILE__ > FLOOFY");
487  EXPECT_EQ(
488      Lexer::getSourceText(CharSourceRange(Results8[0].ConditionRange, false),
489                           SourceMgr, LangOpts),
490      "__FILE__ > FLOOFY");
491}
492
493// namespace
494