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