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