• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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