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