PPCallbacksTest.cpp revision 1a4191d0697098c424646654784399b839f87bc4
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/Basic/Diagnostic.h"
12#include "clang/Basic/FileManager.h"
13#include "clang/Basic/LangOptions.h"
14#include "clang/Basic/SourceManager.h"
15#include "clang/Basic/TargetInfo.h"
16#include "clang/Basic/TargetOptions.h"
17#include "clang/Lex/HeaderSearch.h"
18#include "clang/Lex/HeaderSearchOptions.h"
19#include "clang/Lex/ModuleLoader.h"
20#include "clang/Lex/PreprocessorOptions.h"
21#include "llvm/ADT/SmallString.h"
22#include "llvm/Support/PathV2.h"
23#include "gtest/gtest.h"
24
25using namespace llvm;
26using namespace llvm::sys;
27using namespace clang;
28
29namespace {
30
31// Stub out module loading.
32class VoidModuleLoader : public ModuleLoader {
33  virtual ModuleLoadResult loadModule(SourceLocation ImportLoc,
34                                      ModuleIdPath Path,
35                                      Module::NameVisibilityKind Visibility,
36                                      bool IsInclusionDirective) {
37    return ModuleLoadResult();
38  }
39
40  virtual void makeModuleVisible(Module *Mod,
41                                 Module::NameVisibilityKind Visibility) { }
42};
43
44// Stub to collect data from InclusionDirective callbacks.
45class InclusionDirectiveCallbacks : public PPCallbacks {
46public:
47  void InclusionDirective(SourceLocation HashLoc,
48    const Token &IncludeTok,
49    StringRef FileName,
50    bool IsAngled,
51    CharSourceRange FilenameRange,
52    const FileEntry *File,
53    StringRef SearchPath,
54    StringRef RelativePath,
55    const Module *Imported) {
56      this->HashLoc = HashLoc;
57      this->IncludeTok = IncludeTok;
58      this->FileName = FileName.str();
59      this->IsAngled = IsAngled;
60      this->FilenameRange = FilenameRange;
61      this->File = File;
62      this->SearchPath = SearchPath.str();
63      this->RelativePath = RelativePath.str();
64      this->Imported = Imported;
65  }
66
67  SourceLocation HashLoc;
68  Token IncludeTok;
69  SmallString<16> FileName;
70  bool IsAngled;
71  CharSourceRange FilenameRange;
72  const FileEntry* File;
73  SmallString<16> SearchPath;
74  SmallString<16> RelativePath;
75  const Module* Imported;
76};
77
78// PPCallbacks test fixture.
79class PPCallbacksTest : public ::testing::Test {
80protected:
81  PPCallbacksTest()
82    : FileMgr(FileMgrOpts),
83      DiagID(new DiagnosticIDs()),
84      DiagOpts(new DiagnosticOptions()),
85      Diags(DiagID, DiagOpts.getPtr(), new IgnoringDiagConsumer()),
86      SourceMgr(Diags, FileMgr) {
87    TargetOpts = new TargetOptions();
88    TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
89    Target = TargetInfo::CreateTargetInfo(Diags, &*TargetOpts);
90  }
91
92  FileSystemOptions FileMgrOpts;
93  FileManager FileMgr;
94  IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
95  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
96  DiagnosticsEngine Diags;
97  SourceManager SourceMgr;
98  LangOptions LangOpts;
99  IntrusiveRefCntPtr<TargetOptions> TargetOpts;
100  IntrusiveRefCntPtr<TargetInfo> Target;
101
102  // Register a header path as a known file and add its location
103  // to search path.
104  void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath,
105    bool IsSystemHeader) {
106      // Tell FileMgr about header.
107      FileMgr.getVirtualFile(HeaderPath, 0, 0);
108
109      // Add header's parent path to search path.
110      StringRef SearchPath = path::parent_path(HeaderPath);
111      const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath);
112      DirectoryLookup DL(DE, SrcMgr::C_User, true, false);
113      HeaderInfo.AddSearchPath(DL, IsSystemHeader);
114  }
115
116  // Get the raw source string of the range.
117  StringRef GetSourceString(CharSourceRange Range) {
118    const char* B = SourceMgr.getCharacterData(Range.getBegin());
119    const char* E = SourceMgr.getCharacterData(Range.getEnd());
120
121    return StringRef(B, E - B);
122  }
123
124  // Run lexer over SourceText and collect FilenameRange from
125  // the InclusionDirective callback.
126  CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText,
127      const char* HeaderPath, bool SystemHeader) {
128    MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(SourceText);
129    (void)SourceMgr.createMainFileIDForMemBuffer(Buf);
130
131    VoidModuleLoader ModLoader;
132
133    IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts = new HeaderSearchOptions();
134    HeaderSearch HeaderInfo(HSOpts, FileMgr, Diags, LangOpts, Target.getPtr());
135    AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader);
136
137    IntrusiveRefCntPtr<PreprocessorOptions> PPOpts = new PreprocessorOptions();
138    Preprocessor PP(PPOpts, Diags, LangOpts,
139      Target.getPtr(),
140      SourceMgr, HeaderInfo, ModLoader,
141      /*IILookup =*/ 0,
142      /*OwnsHeaderSearch =*/false,
143      /*DelayInitialization =*/ false);
144    InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks;
145    PP.addPPCallbacks(Callbacks); // Takes ownership.
146
147    // Lex source text.
148    PP.EnterMainSourceFile();
149
150    while (true) {
151      Token Tok;
152      PP.Lex(Tok);
153      if (Tok.is(tok::eof))
154        break;
155    }
156
157    // Callbacks have been executed at this point -- return filename range.
158    return Callbacks->FilenameRange;
159  }
160};
161
162TEST_F(PPCallbacksTest, QuotedFilename) {
163  const char* Source =
164    "#include \"quoted.h\"\n";
165
166  CharSourceRange Range =
167    InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
168
169  ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
170}
171
172TEST_F(PPCallbacksTest, AngledFilename) {
173  const char* Source =
174    "#include <angled.h>\n";
175
176  CharSourceRange Range =
177    InclusionDirectiveFilenameRange(Source, "/angled.h", true);
178
179  ASSERT_EQ("<angled.h>", GetSourceString(Range));
180}
181
182TEST_F(PPCallbacksTest, QuotedInMacro) {
183  const char* Source =
184    "#define MACRO_QUOTED \"quoted.h\"\n"
185    "#include MACRO_QUOTED\n";
186
187  CharSourceRange Range =
188    InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
189
190  ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
191}
192
193TEST_F(PPCallbacksTest, AngledInMacro) {
194  const char* Source =
195    "#define MACRO_ANGLED <angled.h>\n"
196    "#include MACRO_ANGLED\n";
197
198  CharSourceRange Range =
199    InclusionDirectiveFilenameRange(Source, "/angled.h", true);
200
201  ASSERT_EQ("<angled.h>", GetSourceString(Range));
202}
203
204TEST_F(PPCallbacksTest, StringizedMacroArgument) {
205  const char* Source =
206    "#define MACRO_STRINGIZED(x) #x\n"
207    "#include MACRO_STRINGIZED(quoted.h)\n";
208
209  CharSourceRange Range =
210    InclusionDirectiveFilenameRange(Source, "/quoted.h", false);
211
212  ASSERT_EQ("\"quoted.h\"", GetSourceString(Range));
213}
214
215TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) {
216  const char* Source =
217    "#define MACRO_ANGLED <angled.h>\n"
218    "#define MACRO_CONCAT(x, y) x ## _ ## y\n"
219    "#include MACRO_CONCAT(MACRO, ANGLED)\n";
220
221  CharSourceRange Range =
222    InclusionDirectiveFilenameRange(Source, "/angled.h", false);
223
224  ASSERT_EQ("<angled.h>", GetSourceString(Range));
225}
226
227TEST_F(PPCallbacksTest, TrigraphFilename) {
228  const char* Source =
229    "#include \"tri\?\?-graph.h\"\n";
230
231  CharSourceRange Range =
232    InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
233
234  ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
235}
236
237TEST_F(PPCallbacksTest, TrigraphInMacro) {
238  const char* Source =
239    "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n"
240    "#include MACRO_TRIGRAPH\n";
241
242  CharSourceRange Range =
243    InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false);
244
245  ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range));
246}
247
248} // anonoymous namespace
249