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/Path.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