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