|  | //===- unittests/Lex/PPCallbacksTest.cpp - PPCallbacks tests ------===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===--------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/Lex/Preprocessor.h" | 
|  | #include "clang/AST/ASTConsumer.h" | 
|  | #include "clang/AST/ASTContext.h" | 
|  | #include "clang/Basic/Diagnostic.h" | 
|  | #include "clang/Basic/DiagnosticOptions.h" | 
|  | #include "clang/Basic/FileManager.h" | 
|  | #include "clang/Basic/LangOptions.h" | 
|  | #include "clang/Basic/MemoryBufferCache.h" | 
|  | #include "clang/Basic/SourceManager.h" | 
|  | #include "clang/Basic/TargetInfo.h" | 
|  | #include "clang/Basic/TargetOptions.h" | 
|  | #include "clang/Lex/HeaderSearch.h" | 
|  | #include "clang/Lex/HeaderSearchOptions.h" | 
|  | #include "clang/Lex/ModuleLoader.h" | 
|  | #include "clang/Lex/PreprocessorOptions.h" | 
|  | #include "clang/Parse/Parser.h" | 
|  | #include "clang/Sema/Sema.h" | 
|  | #include "llvm/ADT/SmallString.h" | 
|  | #include "llvm/Support/Path.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace clang; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Stub to collect data from InclusionDirective callbacks. | 
|  | class InclusionDirectiveCallbacks : public PPCallbacks { | 
|  | public: | 
|  | void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, | 
|  | StringRef FileName, bool IsAngled, | 
|  | CharSourceRange FilenameRange, const FileEntry *File, | 
|  | StringRef SearchPath, StringRef RelativePath, | 
|  | const Module *Imported, | 
|  | SrcMgr::CharacteristicKind FileType) override { | 
|  | this->HashLoc = HashLoc; | 
|  | this->IncludeTok = IncludeTok; | 
|  | this->FileName = FileName.str(); | 
|  | this->IsAngled = IsAngled; | 
|  | this->FilenameRange = FilenameRange; | 
|  | this->File = File; | 
|  | this->SearchPath = SearchPath.str(); | 
|  | this->RelativePath = RelativePath.str(); | 
|  | this->Imported = Imported; | 
|  | this->FileType = FileType; | 
|  | } | 
|  |  | 
|  | SourceLocation HashLoc; | 
|  | Token IncludeTok; | 
|  | SmallString<16> FileName; | 
|  | bool IsAngled; | 
|  | CharSourceRange FilenameRange; | 
|  | const FileEntry* File; | 
|  | SmallString<16> SearchPath; | 
|  | SmallString<16> RelativePath; | 
|  | const Module* Imported; | 
|  | SrcMgr::CharacteristicKind FileType; | 
|  | }; | 
|  |  | 
|  | // Stub to collect data from PragmaOpenCLExtension callbacks. | 
|  | class PragmaOpenCLExtensionCallbacks : public PPCallbacks { | 
|  | public: | 
|  | typedef struct { | 
|  | SmallString<16> Name; | 
|  | unsigned State; | 
|  | } CallbackParameters; | 
|  |  | 
|  | PragmaOpenCLExtensionCallbacks() : Name("Not called."), State(99) {} | 
|  |  | 
|  | void PragmaOpenCLExtension(clang::SourceLocation NameLoc, | 
|  | const clang::IdentifierInfo *Name, | 
|  | clang::SourceLocation StateLoc, | 
|  | unsigned State) override { | 
|  | this->NameLoc = NameLoc; | 
|  | this->Name = Name->getName(); | 
|  | this->StateLoc = StateLoc; | 
|  | this->State = State; | 
|  | } | 
|  |  | 
|  | SourceLocation NameLoc; | 
|  | SmallString<16> Name; | 
|  | SourceLocation StateLoc; | 
|  | unsigned State; | 
|  | }; | 
|  |  | 
|  | // PPCallbacks test fixture. | 
|  | class PPCallbacksTest : public ::testing::Test { | 
|  | protected: | 
|  | PPCallbacksTest() | 
|  | : InMemoryFileSystem(new vfs::InMemoryFileSystem), | 
|  | FileMgr(FileSystemOptions(), InMemoryFileSystem), | 
|  | DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()), | 
|  | Diags(DiagID, DiagOpts.get(), new IgnoringDiagConsumer()), | 
|  | SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions()) { | 
|  | TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; | 
|  | Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); | 
|  | } | 
|  |  | 
|  | IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem; | 
|  | FileManager FileMgr; | 
|  | IntrusiveRefCntPtr<DiagnosticIDs> DiagID; | 
|  | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; | 
|  | DiagnosticsEngine Diags; | 
|  | SourceManager SourceMgr; | 
|  | LangOptions LangOpts; | 
|  | std::shared_ptr<TargetOptions> TargetOpts; | 
|  | IntrusiveRefCntPtr<TargetInfo> Target; | 
|  |  | 
|  | // Register a header path as a known file and add its location | 
|  | // to search path. | 
|  | void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath, | 
|  | bool IsSystemHeader) { | 
|  | // Tell FileMgr about header. | 
|  | InMemoryFileSystem->addFile(HeaderPath, 0, | 
|  | llvm::MemoryBuffer::getMemBuffer("\n")); | 
|  |  | 
|  | // Add header's parent path to search path. | 
|  | StringRef SearchPath = llvm::sys::path::parent_path(HeaderPath); | 
|  | const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath); | 
|  | DirectoryLookup DL(DE, SrcMgr::C_User, false); | 
|  | HeaderInfo.AddSearchPath(DL, IsSystemHeader); | 
|  | } | 
|  |  | 
|  | // Get the raw source string of the range. | 
|  | StringRef GetSourceString(CharSourceRange Range) { | 
|  | const char* B = SourceMgr.getCharacterData(Range.getBegin()); | 
|  | const char* E = SourceMgr.getCharacterData(Range.getEnd()); | 
|  |  | 
|  | return StringRef(B, E - B); | 
|  | } | 
|  |  | 
|  | // Run lexer over SourceText and collect FilenameRange from | 
|  | // the InclusionDirective callback. | 
|  | CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText, | 
|  | const char* HeaderPath, bool SystemHeader) { | 
|  | std::unique_ptr<llvm::MemoryBuffer> Buf = | 
|  | llvm::MemoryBuffer::getMemBuffer(SourceText); | 
|  | SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); | 
|  |  | 
|  | TrivialModuleLoader ModLoader; | 
|  | MemoryBufferCache PCMCache; | 
|  |  | 
|  | HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, | 
|  | Diags, LangOpts, Target.get()); | 
|  | AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); | 
|  |  | 
|  | Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, | 
|  | SourceMgr, PCMCache, HeaderInfo, ModLoader, | 
|  | /*IILookup =*/nullptr, | 
|  | /*OwnsHeaderSearch =*/false); | 
|  | return InclusionDirectiveCallback(PP)->FilenameRange; | 
|  | } | 
|  |  | 
|  | SrcMgr::CharacteristicKind InclusionDirectiveCharacteristicKind( | 
|  | const char *SourceText, const char *HeaderPath, bool SystemHeader) { | 
|  | std::unique_ptr<llvm::MemoryBuffer> Buf = | 
|  | llvm::MemoryBuffer::getMemBuffer(SourceText); | 
|  | SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); | 
|  |  | 
|  | TrivialModuleLoader ModLoader; | 
|  | MemoryBufferCache PCMCache; | 
|  |  | 
|  | HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, | 
|  | Diags, LangOpts, Target.get()); | 
|  | AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); | 
|  |  | 
|  | Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, | 
|  | SourceMgr, PCMCache, HeaderInfo, ModLoader, | 
|  | /*IILookup =*/nullptr, | 
|  | /*OwnsHeaderSearch =*/false); | 
|  | return InclusionDirectiveCallback(PP)->FileType; | 
|  | } | 
|  |  | 
|  | InclusionDirectiveCallbacks *InclusionDirectiveCallback(Preprocessor &PP) { | 
|  | PP.Initialize(*Target); | 
|  | InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks; | 
|  | PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks)); | 
|  |  | 
|  | // Lex source text. | 
|  | PP.EnterMainSourceFile(); | 
|  |  | 
|  | while (true) { | 
|  | Token Tok; | 
|  | PP.Lex(Tok); | 
|  | if (Tok.is(tok::eof)) | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Callbacks have been executed at this point -- return filename range. | 
|  | return Callbacks; | 
|  | } | 
|  |  | 
|  | PragmaOpenCLExtensionCallbacks::CallbackParameters | 
|  | PragmaOpenCLExtensionCall(const char* SourceText) { | 
|  | LangOptions OpenCLLangOpts; | 
|  | OpenCLLangOpts.OpenCL = 1; | 
|  |  | 
|  | std::unique_ptr<llvm::MemoryBuffer> SourceBuf = | 
|  | llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl"); | 
|  | SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf))); | 
|  |  | 
|  | TrivialModuleLoader ModLoader; | 
|  | MemoryBufferCache PCMCache; | 
|  | HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, | 
|  | Diags, OpenCLLangOpts, Target.get()); | 
|  |  | 
|  | Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, | 
|  | OpenCLLangOpts, SourceMgr, PCMCache, HeaderInfo, ModLoader, | 
|  | /*IILookup =*/nullptr, | 
|  | /*OwnsHeaderSearch =*/false); | 
|  | PP.Initialize(*Target); | 
|  |  | 
|  | // parser actually sets correct pragma handlers for preprocessor | 
|  | // according to LangOptions, so we init Parser to register opencl | 
|  | // pragma handlers | 
|  | ASTContext Context(OpenCLLangOpts, SourceMgr, | 
|  | PP.getIdentifierTable(), PP.getSelectorTable(), | 
|  | PP.getBuiltinInfo()); | 
|  | Context.InitBuiltinTypes(*Target); | 
|  |  | 
|  | ASTConsumer Consumer; | 
|  | Sema S(PP, Context, Consumer); | 
|  | Parser P(PP, S, false); | 
|  | PragmaOpenCLExtensionCallbacks* Callbacks = new PragmaOpenCLExtensionCallbacks; | 
|  | PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks)); | 
|  |  | 
|  | // Lex source text. | 
|  | PP.EnterMainSourceFile(); | 
|  | while (true) { | 
|  | Token Tok; | 
|  | PP.Lex(Tok); | 
|  | if (Tok.is(tok::eof)) | 
|  | break; | 
|  | } | 
|  |  | 
|  | PragmaOpenCLExtensionCallbacks::CallbackParameters RetVal = { | 
|  | Callbacks->Name, | 
|  | Callbacks->State | 
|  | }; | 
|  | return RetVal; | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_F(PPCallbacksTest, UserFileCharacteristics) { | 
|  | const char *Source = "#include \"quoted.h\"\n"; | 
|  |  | 
|  | SrcMgr::CharacteristicKind Kind = | 
|  | InclusionDirectiveCharacteristicKind(Source, "/quoted.h", false); | 
|  |  | 
|  | ASSERT_EQ(SrcMgr::CharacteristicKind::C_User, Kind); | 
|  | } | 
|  |  | 
|  | TEST_F(PPCallbacksTest, QuotedFilename) { | 
|  | const char* Source = | 
|  | "#include \"quoted.h\"\n"; | 
|  |  | 
|  | CharSourceRange Range = | 
|  | InclusionDirectiveFilenameRange(Source, "/quoted.h", false); | 
|  |  | 
|  | ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); | 
|  | } | 
|  |  | 
|  | TEST_F(PPCallbacksTest, AngledFilename) { | 
|  | const char* Source = | 
|  | "#include <angled.h>\n"; | 
|  |  | 
|  | CharSourceRange Range = | 
|  | InclusionDirectiveFilenameRange(Source, "/angled.h", true); | 
|  |  | 
|  | ASSERT_EQ("<angled.h>", GetSourceString(Range)); | 
|  | } | 
|  |  | 
|  | TEST_F(PPCallbacksTest, QuotedInMacro) { | 
|  | const char* Source = | 
|  | "#define MACRO_QUOTED \"quoted.h\"\n" | 
|  | "#include MACRO_QUOTED\n"; | 
|  |  | 
|  | CharSourceRange Range = | 
|  | InclusionDirectiveFilenameRange(Source, "/quoted.h", false); | 
|  |  | 
|  | ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); | 
|  | } | 
|  |  | 
|  | TEST_F(PPCallbacksTest, AngledInMacro) { | 
|  | const char* Source = | 
|  | "#define MACRO_ANGLED <angled.h>\n" | 
|  | "#include MACRO_ANGLED\n"; | 
|  |  | 
|  | CharSourceRange Range = | 
|  | InclusionDirectiveFilenameRange(Source, "/angled.h", true); | 
|  |  | 
|  | ASSERT_EQ("<angled.h>", GetSourceString(Range)); | 
|  | } | 
|  |  | 
|  | TEST_F(PPCallbacksTest, StringizedMacroArgument) { | 
|  | const char* Source = | 
|  | "#define MACRO_STRINGIZED(x) #x\n" | 
|  | "#include MACRO_STRINGIZED(quoted.h)\n"; | 
|  |  | 
|  | CharSourceRange Range = | 
|  | InclusionDirectiveFilenameRange(Source, "/quoted.h", false); | 
|  |  | 
|  | ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); | 
|  | } | 
|  |  | 
|  | TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) { | 
|  | const char* Source = | 
|  | "#define MACRO_ANGLED <angled.h>\n" | 
|  | "#define MACRO_CONCAT(x, y) x ## _ ## y\n" | 
|  | "#include MACRO_CONCAT(MACRO, ANGLED)\n"; | 
|  |  | 
|  | CharSourceRange Range = | 
|  | InclusionDirectiveFilenameRange(Source, "/angled.h", false); | 
|  |  | 
|  | ASSERT_EQ("<angled.h>", GetSourceString(Range)); | 
|  | } | 
|  |  | 
|  | TEST_F(PPCallbacksTest, TrigraphFilename) { | 
|  | const char* Source = | 
|  | "#include \"tri\?\?-graph.h\"\n"; | 
|  |  | 
|  | CharSourceRange Range = | 
|  | InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false); | 
|  |  | 
|  | ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); | 
|  | } | 
|  |  | 
|  | TEST_F(PPCallbacksTest, TrigraphInMacro) { | 
|  | const char* Source = | 
|  | "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n" | 
|  | "#include MACRO_TRIGRAPH\n"; | 
|  |  | 
|  | CharSourceRange Range = | 
|  | InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false); | 
|  |  | 
|  | ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); | 
|  | } | 
|  |  | 
|  | TEST_F(PPCallbacksTest, OpenCLExtensionPragmaEnabled) { | 
|  | const char* Source = | 
|  | "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"; | 
|  |  | 
|  | PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters = | 
|  | PragmaOpenCLExtensionCall(Source); | 
|  |  | 
|  | ASSERT_EQ("cl_khr_fp64", Parameters.Name); | 
|  | unsigned ExpectedState = 1; | 
|  | ASSERT_EQ(ExpectedState, Parameters.State); | 
|  | } | 
|  |  | 
|  | TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) { | 
|  | const char* Source = | 
|  | "#pragma OPENCL EXTENSION cl_khr_fp16 : disable\n"; | 
|  |  | 
|  | PragmaOpenCLExtensionCallbacks::CallbackParameters Parameters = | 
|  | PragmaOpenCLExtensionCall(Source); | 
|  |  | 
|  | ASSERT_EQ("cl_khr_fp16", Parameters.Name); | 
|  | unsigned ExpectedState = 0; | 
|  | ASSERT_EQ(ExpectedState, Parameters.State); | 
|  | } | 
|  |  | 
|  | } // anonoymous namespace |