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 }; 44 45 // Stub to collect data from InclusionDirective callbacks. 46 class InclusionDirectiveCallbacks : public PPCallbacks { 47 public: 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: 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. 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. 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. 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 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 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 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 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 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 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 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 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