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/Basic/Diagnostic.h" 12 #include "clang/Basic/FileManager.h" 13 #include "clang/Basic/LangOptions.h" 14 #include "clang/Basic/SourceManager.h" 15 #include "clang/Basic/TargetInfo.h" 16 #include "clang/Basic/TargetOptions.h" 17 #include "clang/Lex/HeaderSearch.h" 18 #include "clang/Lex/HeaderSearchOptions.h" 19 #include "clang/Lex/ModuleLoader.h" 20 #include "clang/Lex/PreprocessorOptions.h" 21 #include "clang/Parse/Parser.h" 22 #include "clang/Sema/Sema.h" 23 #include "clang/AST/ASTContext.h" 24 #include "clang/AST/ASTConsumer.h" 25 #include "llvm/ADT/SmallString.h" 26 #include "llvm/Support/Path.h" 27 #include "gtest/gtest.h" 28 29 using namespace llvm; 30 using namespace llvm::sys; 31 using namespace clang; 32 33 namespace { 34 35 // Stub out module loading. 36 class VoidModuleLoader : public ModuleLoader { 37 virtual ModuleLoadResult loadModule(SourceLocation ImportLoc, 38 ModuleIdPath Path, 39 Module::NameVisibilityKind Visibility, 40 bool IsInclusionDirective) { 41 return ModuleLoadResult(); 42 } 43 44 virtual void makeModuleVisible(Module *Mod, 45 Module::NameVisibilityKind Visibility, 46 SourceLocation ImportLoc, 47 bool Complain) { } 48 }; 49 50 // Stub to collect data from InclusionDirective callbacks. 51 class InclusionDirectiveCallbacks : public PPCallbacks { 52 public: 53 void InclusionDirective(SourceLocation HashLoc, 54 const Token &IncludeTok, 55 StringRef FileName, 56 bool IsAngled, 57 CharSourceRange FilenameRange, 58 const FileEntry *File, 59 StringRef SearchPath, 60 StringRef RelativePath, 61 const Module *Imported) { 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( 95 clang::SourceLocation NameLoc, const clang::IdentifierInfo *Name, 96 clang::SourceLocation StateLoc, unsigned State) { 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), 114 DiagID(new DiagnosticIDs()), 115 DiagOpts(new DiagnosticOptions()), 116 Diags(DiagID, DiagOpts.getPtr(), new IgnoringDiagConsumer()), 117 SourceMgr(Diags, FileMgr) { 118 TargetOpts = new TargetOptions(); 119 TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; 120 Target = TargetInfo::CreateTargetInfo(Diags, &*TargetOpts); 121 } 122 123 FileSystemOptions FileMgrOpts; 124 FileManager FileMgr; 125 IntrusiveRefCntPtr<DiagnosticIDs> DiagID; 126 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; 127 DiagnosticsEngine Diags; 128 SourceManager SourceMgr; 129 LangOptions LangOpts; 130 IntrusiveRefCntPtr<TargetOptions> TargetOpts; 131 IntrusiveRefCntPtr<TargetInfo> Target; 132 133 // Register a header path as a known file and add its location 134 // to search path. 135 void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath, 136 bool IsSystemHeader) { 137 // Tell FileMgr about header. 138 FileMgr.getVirtualFile(HeaderPath, 0, 0); 139 140 // Add header's parent path to search path. 141 StringRef SearchPath = path::parent_path(HeaderPath); 142 const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath); 143 DirectoryLookup DL(DE, SrcMgr::C_User, false); 144 HeaderInfo.AddSearchPath(DL, IsSystemHeader); 145 } 146 147 // Get the raw source string of the range. 148 StringRef GetSourceString(CharSourceRange Range) { 149 const char* B = SourceMgr.getCharacterData(Range.getBegin()); 150 const char* E = SourceMgr.getCharacterData(Range.getEnd()); 151 152 return StringRef(B, E - B); 153 } 154 155 // Run lexer over SourceText and collect FilenameRange from 156 // the InclusionDirective callback. 157 CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText, 158 const char* HeaderPath, bool SystemHeader) { 159 MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(SourceText); 160 (void)SourceMgr.createMainFileIDForMemBuffer(Buf); 161 162 VoidModuleLoader ModLoader; 163 164 IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts = new HeaderSearchOptions(); 165 HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, 166 Target.getPtr()); 167 AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); 168 169 IntrusiveRefCntPtr<PreprocessorOptions> PPOpts = new PreprocessorOptions(); 170 Preprocessor PP(PPOpts, Diags, LangOpts, 171 Target.getPtr(), 172 SourceMgr, HeaderInfo, ModLoader, 173 /*IILookup =*/ 0, 174 /*OwnsHeaderSearch =*/false, 175 /*DelayInitialization =*/ false); 176 InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks; 177 PP.addPPCallbacks(Callbacks); // Takes ownership. 178 179 // Lex source text. 180 PP.EnterMainSourceFile(); 181 182 while (true) { 183 Token Tok; 184 PP.Lex(Tok); 185 if (Tok.is(tok::eof)) 186 break; 187 } 188 189 // Callbacks have been executed at this point -- return filename range. 190 return Callbacks->FilenameRange; 191 } 192 193 PragmaOpenCLExtensionCallbacks::CallbackParameters 194 PragmaOpenCLExtensionCall(const char* SourceText) { 195 LangOptions OpenCLLangOpts; 196 OpenCLLangOpts.OpenCL = 1; 197 198 MemoryBuffer* sourceBuf = MemoryBuffer::getMemBuffer(SourceText, "test.cl"); 199 (void)SourceMgr.createMainFileIDForMemBuffer(sourceBuf); 200 201 VoidModuleLoader ModLoader; 202 HeaderSearch HeaderInfo(new HeaderSearchOptions, SourceMgr, Diags, 203 OpenCLLangOpts, Target.getPtr()); 204 205 Preprocessor PP(new PreprocessorOptions(), Diags, OpenCLLangOpts, 206 Target.getPtr(), 207 SourceMgr, HeaderInfo, ModLoader, 208 /*IILookup =*/ 0, 209 /*OwnsHeaderSearch =*/false, 210 /*DelayInitialization =*/ false); 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, Target.getPtr(), 216 PP.getIdentifierTable(), PP.getSelectorTable(), 217 PP.getBuiltinInfo(), 0); 218 ASTConsumer Consumer; 219 Sema S(PP, Context, Consumer); 220 Parser P(PP, S, false); 221 PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks; 222 PP.addPPCallbacks(Callbacks); // Takes ownership. 223 224 // Lex source text. 225 PP.EnterMainSourceFile(); 226 while (true) { 227 Token Tok; 228 PP.Lex(Tok); 229 if (Tok.is(tok::eof)) 230 break; 231 } 232 233 PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = { 234 Callbacks->Name, 235 Callbacks->State 236 }; 237 return RetVal; 238 } 239 }; 240 241 TEST_F(PPCallbacksTest, QuotedFilename) { 242 const char* Source = 243 "#include \"quoted.h\"\n"; 244 245 CharSourceRange Range = 246 InclusionDirectiveFilenameRange(Source, "/quoted.h", false); 247 248 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); 249 } 250 251 TEST_F(PPCallbacksTest, AngledFilename) { 252 const char* Source = 253 "#include <angled.h>\n"; 254 255 CharSourceRange Range = 256 InclusionDirectiveFilenameRange(Source, "/angled.h", true); 257 258 ASSERT_EQ("<angled.h>", GetSourceString(Range)); 259 } 260 261 TEST_F(PPCallbacksTest, QuotedInMacro) { 262 const char* Source = 263 "#define MACRO_QUOTED \"quoted.h\"\n" 264 "#include MACRO_QUOTED\n"; 265 266 CharSourceRange Range = 267 InclusionDirectiveFilenameRange(Source, "/quoted.h", false); 268 269 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); 270 } 271 272 TEST_F(PPCallbacksTest, AngledInMacro) { 273 const char* Source = 274 "#define MACRO_ANGLED <angled.h>\n" 275 "#include MACRO_ANGLED\n"; 276 277 CharSourceRange Range = 278 InclusionDirectiveFilenameRange(Source, "/angled.h", true); 279 280 ASSERT_EQ("<angled.h>", GetSourceString(Range)); 281 } 282 283 TEST_F(PPCallbacksTest, StringizedMacroArgument) { 284 const char* Source = 285 "#define MACRO_STRINGIZED(x) #x\n" 286 "#include MACRO_STRINGIZED(quoted.h)\n"; 287 288 CharSourceRange Range = 289 InclusionDirectiveFilenameRange(Source, "/quoted.h", false); 290 291 ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); 292 } 293 294 TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) { 295 const char* Source = 296 "#define MACRO_ANGLED <angled.h>\n" 297 "#define MACRO_CONCAT(x, y) x ## _ ## y\n" 298 "#include MACRO_CONCAT(MACRO, ANGLED)\n"; 299 300 CharSourceRange Range = 301 InclusionDirectiveFilenameRange(Source, "/angled.h", false); 302 303 ASSERT_EQ("<angled.h>", GetSourceString(Range)); 304 } 305 306 TEST_F(PPCallbacksTest, TrigraphFilename) { 307 const char* Source = 308 "#include \"tri\?\?-graph.h\"\n"; 309 310 CharSourceRange Range = 311 InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false); 312 313 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); 314 } 315 316 TEST_F(PPCallbacksTest, TrigraphInMacro) { 317 const char* Source = 318 "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n" 319 "#include MACRO_TRIGRAPH\n"; 320 321 CharSourceRange Range = 322 InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false); 323 324 ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); 325 } 326 327 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) { 328 const char* Source = 329 "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"; 330 331 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters = 332 PragmaOpenCLExtensionCall(Source); 333 334 ASSERT_EQ("cl_khr_fp64", Parameters.Name); 335 unsigned ExpectedState = 1; 336 ASSERT_EQ(ExpectedState, Parameters.State); 337 } 338 339 TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) { 340 const char* Source = 341 "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n"; 342 343 PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters = 344 PragmaOpenCLExtensionCall(Source); 345 346 ASSERT_EQ("cl_khr_fp16", Parameters.Name); 347 unsigned ExpectedState = 0; 348 ASSERT_EQ(ExpectedState, Parameters.State); 349 } 350 351 } // anonoymous namespace 352