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