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