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 llvm; 31 using namespace llvm::sys; 32 using namespace clang; 33 34 namespace { 35 36 // Stub out module loading. 37 class VoidModuleLoader : public ModuleLoader { 38 ModuleLoadResult loadModule(SourceLocation ImportLoc, 39 ModuleIdPath Path, 40 Module::NameVisibilityKind Visibility, 41 bool IsInclusionDirective) override { 42 return ModuleLoadResult(); 43 } 44 45 void makeModuleVisible(Module *Mod, 46 Module::NameVisibilityKind Visibility, 47 SourceLocation ImportLoc, 48 bool Complain) override { } 49 50 GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override 51 { return nullptr; } 52 bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override 53 { return 0; }; 54 }; 55 56 // Stub to collect data from InclusionDirective callbacks. 57 class InclusionDirectiveCallbacks : public PPCallbacks { 58 public: 59 void InclusionDirective(SourceLocation HashLoc, 60 const Token &IncludeTok, 61 StringRef FileName, 62 bool IsAngled, 63 CharSourceRange FilenameRange, 64 const FileEntry *File, 65 StringRef SearchPath, 66 StringRef RelativePath, 67 const Module *Imported) { 68 this->HashLoc = HashLoc; 69 this->IncludeTok = IncludeTok; 70 this->FileName = FileName.str(); 71 this->IsAngled = IsAngled; 72 this->FilenameRange = FilenameRange; 73 this->File = File; 74 this->SearchPath = SearchPath.str(); 75 this->RelativePath = RelativePath.str(); 76 this->Imported = Imported; 77 } 78 79 SourceLocation HashLoc; 80 Token IncludeTok; 81 SmallString<16> FileName; 82 bool IsAngled; 83 CharSourceRange FilenameRange; 84 const FileEntry* File; 85 SmallString<16> SearchPath; 86 SmallString<16> RelativePath; 87 const Module* Imported; 88 }; 89 90 // Stub to collect data from PragmaOpenCLExtension callbacks. 91 class PragmaOpenCLExtensionCallbacks : public PPCallbacks { 92 public: 93 typedef struct { 94 SmallString<16> Name; 95 unsigned State; 96 } CallbackParameters; 97 98 PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {}; 99 100 void PragmaOpenCLExtension( 101 clang::SourceLocation NameLoc, const clang::IdentifierInfo *Name, 102 clang::SourceLocation StateLoc, unsigned State) { 103 this->NameLoc = NameLoc; 104 this->Name = Name->getName(); 105 this->StateLoc = StateLoc; 106 this->State = State; 107 }; 108 109 SourceLocation NameLoc; 110 SmallString<16> Name; 111 SourceLocation StateLoc; 112 unsigned State; 113 }; 114 115 // PPCallbacks test fixture. 116 class PPCallbacksTest : public ::testing::Test { 117 protected: 118 PPCallbacksTest() 119 : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()), 120 DiagOpts(new DiagnosticOptions()), 121 Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()), 122 SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) { 123 TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; 124 Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); 125 } 126 127 FileSystemOptions FileMgrOpts; 128 FileManager FileMgr; 129 IntrusiveRefCntPtr<DiagnosticIDs> DiagID; 130 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; 131 DiagnosticsEngine Diags; 132 SourceManager SourceMgr; 133 LangOptions LangOpts; 134 std::shared_ptr<TargetOptions> TargetOpts; 135 IntrusiveRefCntPtr<TargetInfo> Target; 136 137 // Register a header path as a known file and add its location 138 // to search path. 139 void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath, 140 bool IsSystemHeader) { 141 // Tell FileMgr about header. 142 FileMgr.getVirtualFile(HeaderPath, 0, 0); 143 144 // Add header's parent path to search path. 145 StringRef SearchPath = path::parent_path(HeaderPath); 146 const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath); 147 DirectoryLookup DL(DE, SrcMgr::C_User, false); 148 HeaderInfo.AddSearchPath(DL, IsSystemHeader); 149 } 150 151 // Get the raw source string of the range. 152 StringRef GetSourceString(CharSourceRange Range) { 153 const char* B = SourceMgr.getCharacterData(Range.getBegin()); 154 const char* E = SourceMgr.getCharacterData(Range.getEnd()); 155 156 return StringRef(B, E - B); 157 } 158 159 // Run lexer over SourceText and collect FilenameRange from 160 // the InclusionDirective callback. 161 CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText, 162 const char* HeaderPath, bool SystemHeader) { 163 MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(SourceText); 164 SourceMgr.setMainFileID(SourceMgr.createFileID(Buf)); 165 166 VoidModuleLoader ModLoader; 167 168 IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts = new HeaderSearchOptions(); 169 HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, 170 Target.get()); 171 AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); 172 173 IntrusiveRefCntPtr<PreprocessorOptions> PPOpts = new PreprocessorOptions(); 174 Preprocessor PP(PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, 175 /*IILookup =*/nullptr, 176 /*OwnsHeaderSearch =*/false); 177 PP.Initialize(*Target); 178 InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks; 179 PP.addPPCallbacks(Callbacks); // Takes ownership. 180 181 // Lex source text. 182 PP.EnterMainSourceFile(); 183 184 while (true) { 185 Token Tok; 186 PP.Lex(Tok); 187 if (Tok.is(tok::eof)) 188 break; 189 } 190 191 // Callbacks have been executed at this point -- return filename range. 192 return Callbacks->FilenameRange; 193 } 194 195 PragmaOpenCLExtensionCallbacks::CallbackParameters 196 PragmaOpenCLExtensionCall(const char* SourceText) { 197 LangOptions OpenCLLangOpts; 198 OpenCLLangOpts.OpenCL = 1; 199 200 MemoryBuffer* sourceBuf = MemoryBuffer::getMemBuffer(SourceText, "test.cl"); 201 SourceMgr.setMainFileID(SourceMgr.createFileID(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(Callbacks); // Takes ownership. 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