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