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