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/AST/ASTConsumer.h" 12 #include "clang/AST/ASTContext.h" 13 #include "clang/Basic/Diagnostic.h" 14 #include "clang/Basic/DiagnosticOptions.h" 15 #include "clang/Basic/FileManager.h" 16 #include "clang/Basic/LangOptions.h" 17 #include "clang/Basic/MemoryBufferCache.h" 18 #include "clang/Basic/SourceManager.h" 19 #include "clang/Basic/TargetInfo.h" 20 #include "clang/Basic/TargetOptions.h" 21 #include "clang/Lex/HeaderSearch.h" 22 #include "clang/Lex/HeaderSearchOptions.h" 23 #include "clang/Lex/ModuleLoader.h" 24 #include "clang/Lex/PreprocessorOptions.h" 25 #include "clang/Parse/Parser.h" 26 #include "clang/Sema/Sema.h" 27 #include "llvm/ADT/SmallString.h" 28 #include "llvm/Support/Path.h" 29 #include "gtest/gtest.h" 30 31 using namespace clang; 32 33 namespace { 34 35 // Stub to collect data from InclusionDirective callbacks. 36 class InclusionDirectiveCallbacks : public PPCallbacks { 37 public: 38 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, 39 StringRef FileName, bool IsAngled, 40 CharSourceRange FilenameRange, const FileEntry *File, 41 StringRef SearchPath, StringRef RelativePath, 42 const Module *Imported, 43 SrcMgr::CharacteristicKind FileType) override { 44 this->HashLoc = HashLoc; 45 this->IncludeTok = IncludeTok; 46 this->FileName = FileName.str(); 47 this->IsAngled = IsAngled; 48 this->FilenameRange = FilenameRange; 49 this->File = File; 50 this->SearchPath = SearchPath.str(); 51 this->RelativePath = RelativePath.str(); 52 this->Imported = Imported; 53 this->FileType = FileType; 54 } 55 56 SourceLocation HashLoc; 57 Token IncludeTok; 58 SmallString<16> FileName; 59 bool IsAngled; 60 CharSourceRange FilenameRange; 61 const FileEntry* File; 62 SmallString<16> SearchPath; 63 SmallString<16> RelativePath; 64 const Module* Imported; 65 SrcMgr::CharacteristicKind FileType; 66 }; 67 68 // Stub to collect data from PragmaOpenCLExtension callbacks. 69 class PragmaOpenCLExtensionCallbacks : public PPCallbacks { 70 public: 71 typedef struct { 72 SmallString<16> Name; 73 unsigned State; 74 } CallbackParameters; 75 76 PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {} 77 78 void PragmaOpenCLExtension(clang::SourceLocation NameLoc, 79 const clang::IdentifierInfo *Name, 80 clang::SourceLocation StateLoc, 81 unsigned State) override { 82 this->NameLoc = NameLoc; 83 this->Name = Name->getName(); 84 this->StateLoc = StateLoc; 85 this->State = State; 86 } 87 88 SourceLocation NameLoc; 89 SmallString<16> Name; 90 SourceLocation StateLoc; 91 unsigned State; 92 }; 93 94 // PPCallbacks test fixture. 95 class PPCallbacksTest : public ::testing::Test { 96 protected: 97 PPCallbacksTest() 98 : InMemoryFileSystem(new vfs::InMemoryFileSystem), 99 FileMgr(FileSystemOptions(), InMemoryFileSystem), 100 DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()), 101 Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()), 102 SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) { 103 TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; 104 Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); 105 } 106 107 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem; 108 FileManager FileMgr; 109 IntrusiveRefCntPtr<DiagnosticIDs> DiagID; 110 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; 111 DiagnosticsEngine Diags; 112 SourceManager SourceMgr; 113 LangOptions LangOpts; 114 std::shared_ptr<TargetOptions> TargetOpts; 115 IntrusiveRefCntPtr<TargetInfo> Target; 116 117 // Register a header path as a known file and add its location 118 // to search path. 119 void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath, 120 bool IsSystemHeader) { 121 // Tell FileMgr about header. 122 InMemoryFileSystem->addFile(HeaderPath, 0, 123 llvm::MemoryBuffer::getMemBuffer("\n")); 124 125 // Add header's parent path to search path. 126 StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath); 127 const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath); 128 DirectoryLookup DL(DE, SrcMgr::C_User, false); 129 HeaderInfo.AddSearchPath(DL, IsSystemHeader); 130 } 131 132 // Get the raw source string of the range. 133 StringRef GetSourceString(CharSourceRange Range) { 134 const char* B = SourceMgr.getCharacterData(Range.getBegin()); 135 const char* E = SourceMgr.getCharacterData(Range.getEnd()); 136 137 return StringRef(B, E - B); 138 } 139 140 // Run lexer over SourceText and collect FilenameRange from 141 // the InclusionDirective callback. 142 CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText, 143 const char* HeaderPath, bool SystemHeader) { 144 std::unique_ptr<llvm::MemoryBuffer> Buf = 145 llvm::MemoryBuffer::getMemBuffer(SourceText); 146 SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); 147 148 TrivialModuleLoader ModLoader; 149 MemoryBufferCache PCMCache; 150 151 HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, 152 Diags, LangOpts, Target.get()); 153 AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); 154 155 Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, 156 SourceMgr, PCMCache, HeaderInfo, ModLoader, 157 /*IILookup =*/nullptr, 158 /*OwnsHeaderSearch =*/false); 159 return InclusionDirectiveCallback(PP)->FilenameRange; 160 } 161 162 SrcMgr::CharacteristicKind InclusionDirectiveCharacteristicKind( 163 const char *SourceText, const char *HeaderPath, bool SystemHeader) { 164 std::unique_ptr<llvm::MemoryBuffer> Buf = 165 llvm::MemoryBuffer::getMemBuffer(SourceText); 166 SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); 167 168 TrivialModuleLoader ModLoader; 169 MemoryBufferCache PCMCache; 170 171 HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, 172 Diags, LangOpts, Target.get()); 173 AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); 174 175 Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, 176 SourceMgr, PCMCache, HeaderInfo, ModLoader, 177 /*IILookup =*/nullptr, 178 /*OwnsHeaderSearch =*/false); 179 return InclusionDirectiveCallback(PP)->FileType; 180 } 181 182 InclusionDirectiveCallbacks *InclusionDirectiveCallback(Preprocessor &PP) { 183 PP.Initialize(*Target); 184 InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks; 185 PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks)); 186 187 // Lex source text. 188 PP.EnterMainSourceFile(); 189 190 while (true) { 191 Token Tok; 192 PP.Lex(Tok); 193 if (Tok.is(tok::eof)) 194 break; 195 } 196 197 // Callbacks have been executed at this point -- return filename range. 198 return Callbacks; 199 } 200 201 PragmaOpenCLExtensionCallbacks::CallbackParameters 202 PragmaOpenCLExtensionCall(const char* SourceText) { 203 LangOptions OpenCLLangOpts; 204 OpenCLLangOpts.OpenCL = 1; 205 206 std::unique_ptr<llvm::MemoryBuffer> SourceBuf = 207 llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl"); 208 SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf))); 209 210 TrivialModuleLoader ModLoader; 211 MemoryBufferCache PCMCache; 212 HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, 213 Diags, OpenCLLangOpts, Target.get()); 214 215 Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, 216 OpenCLLangOpts, SourceMgr, PCMCache, HeaderInfo, ModLoader, 217 /*IILookup =*/nullptr, 218 /*OwnsHeaderSearch =*/false); 219 PP.Initialize(*Target); 220 221 // parser actually sets correct pragma handlers for preprocessor 222 // according to LangOptions, so we init Parser to register opencl 223 // pragma handlers 224 ASTContext Context(OpenCLLangOpts, SourceMgr, 225 PP.getIdentifierTable(), PP.getSelectorTable(), 226 PP.getBuiltinInfo()); 227 Context.InitBuiltinTypes(*Target); 228 229 ASTConsumer Consumer; 230 Sema S(PP, Context, Consumer); 231 Parser P(PP, S, false); 232 PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks; 233 PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks)); 234 235 // Lex source text. 236 PP.EnterMainSourceFile(); 237 while (true) { 238 Token Tok; 239 PP.Lex(Tok); 240 if (Tok.is(tok::eof)) 241 break; 242 } 243 244 PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = { 245 Callbacks->Name, 246 Callbacks->State 247 }; 248 return RetVal; 249 } 250 }; 251 252 TEST_F(PPCallbacksTest, UserFileCharacteristics) { 253 const char *Source = "#include \"quoted.h\"\n"; 254 255 SrcMgr::CharacteristicKind Kind = 256 InclusionDirectiveCharacteristicKind(Source, "/quoted.h", false); 257 258 ASSERT_EQ(SrcMgr::CharacteristicKind::C_User, Kind); 259 } 260 261 TEST_F(PPCallbacksTest, QuotedFilename) { 262 const char* Source = 263 "#include \"quoted.h\"\n"; 264 265 CharSourceRange Range = 266 InclusionDirectiveFilenameRange(Source, "/quoted.h", false); 267 268 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); 269 } 270 271 TEST_F(PPCallbacksTest, AngledFilename) { 272 const char* Source = 273 "#include <angled.h>\n"; 274 275 CharSourceRange Range = 276 InclusionDirectiveFilenameRange(Source, "/angled.h", true); 277 278 ASSERT_EQ("<angled.h>", GetSourceString(Range)); 279 } 280 281 TEST_F(PPCallbacksTest, QuotedInMacro) { 282 const char* Source = 283 "#define MACRO_QUOTED \"quoted.h\"\n" 284 "#include MACRO_QUOTED\n"; 285 286 CharSourceRange Range = 287 InclusionDirectiveFilenameRange(Source, "/quoted.h", false); 288 289 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); 290 } 291 292 TEST_F(PPCallbacksTest, AngledInMacro) { 293 const char* Source = 294 "#define MACRO_ANGLED <angled.h>\n" 295 "#include MACRO_ANGLED\n"; 296 297 CharSourceRange Range = 298 InclusionDirectiveFilenameRange(Source, "/angled.h", true); 299 300 ASSERT_EQ("<angled.h>", GetSourceString(Range)); 301 } 302 303 TEST_F(PPCallbacksTest, StringizedMacroArgument) { 304 const char* Source = 305 "#define MACRO_STRINGIZED(x) #x\n" 306 "#include MACRO_STRINGIZED(quoted.h)\n"; 307 308 CharSourceRange Range = 309 InclusionDirectiveFilenameRange(Source, "/quoted.h", false); 310 311 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); 312 } 313 314 TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) { 315 const char* Source = 316 "#define MACRO_ANGLED <angled.h>\n" 317 "#define MACRO_CONCAT(x, y) x ## _ ## y\n" 318 "#include MACRO_CONCAT(MACRO, ANGLED)\n"; 319 320 CharSourceRange Range = 321 InclusionDirectiveFilenameRange(Source, "/angled.h", false); 322 323 ASSERT_EQ("<angled.h>", GetSourceString(Range)); 324 } 325 326 TEST_F(PPCallbacksTest, TrigraphFilename) { 327 const char* Source = 328 "#include \"tri\?\?-graph.h\"\n"; 329 330 CharSourceRange Range = 331 InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false); 332 333 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); 334 } 335 336 TEST_F(PPCallbacksTest, TrigraphInMacro) { 337 const char* Source = 338 "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n" 339 "#include MACRO_TRIGRAPH\n"; 340 341 CharSourceRange Range = 342 InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false); 343 344 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); 345 } 346 347 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) { 348 const char* Source = 349 "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"; 350 351 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters = 352 PragmaOpenCLExtensionCall(Source); 353 354 ASSERT_EQ("cl_khr_fp64", Parameters.Name); 355 unsigned ExpectedState = 1; 356 ASSERT_EQ(ExpectedState, Parameters.State); 357 } 358 359 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) { 360 const char* Source = 361 "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n"; 362 363 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters = 364 PragmaOpenCLExtensionCall(Source); 365 366 ASSERT_EQ("cl_khr_fp16", Parameters.Name); 367 unsigned ExpectedState = 0; 368 ASSERT_EQ(ExpectedState, Parameters.State); 369 } 370 371 } // anonoymous namespace 372