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