//===- 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/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.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/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/PathV2.h" #include "gtest/gtest.h" using namespace llvm; using namespace llvm::sys; using namespace clang; namespace { // Stub out module loading. class VoidModuleLoader : public ModuleLoader { virtual Module *loadModule(SourceLocation ImportLoc, ModuleIdPath Path, Module::NameVisibilityKind Visibility, bool IsInclusionDirective) { return 0; } }; // 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) { 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; } SourceLocation HashLoc; Token IncludeTok; SmallString<16> FileName; bool IsAngled; CharSourceRange FilenameRange; const FileEntry* File; SmallString<16> SearchPath; SmallString<16> RelativePath; const Module* Imported; }; // PPCallbacks test fixture. class PPCallbacksTest : public ::testing::Test { protected: PPCallbacksTest() : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()), DiagOpts(new DiagnosticOptions()), Diags(DiagID, DiagOpts.getPtr(), new IgnoringDiagConsumer()), SourceMgr(Diags, FileMgr) { TargetOpts = new TargetOptions(); TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; Target = TargetInfo::CreateTargetInfo(Diags, *TargetOpts); } FileSystemOptions FileMgrOpts; FileManager FileMgr; IntrusiveRefCntPtr DiagID; IntrusiveRefCntPtr DiagOpts; DiagnosticsEngine Diags; SourceManager SourceMgr; LangOptions LangOpts; IntrusiveRefCntPtr TargetOpts; IntrusiveRefCntPtr 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. FileMgr.getVirtualFile(HeaderPath, 0, 0); // Add header's parent path to search path. StringRef SearchPath = path::parent_path(HeaderPath); const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath); DirectoryLookup DL(DE, SrcMgr::C_User, true, 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) { MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(SourceText); (void)SourceMgr.createMainFileIDForMemBuffer(Buf); VoidModuleLoader ModLoader; IntrusiveRefCntPtr HSOpts = new HeaderSearchOptions(); HeaderSearch HeaderInfo(HSOpts, FileMgr, Diags, LangOpts, Target.getPtr()); AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); IntrusiveRefCntPtr PPOpts = new PreprocessorOptions(); Preprocessor PP(PPOpts, Diags, LangOpts, Target.getPtr(), SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/ 0, /*OwnsHeaderSearch =*/false, /*DelayInitialization =*/ false); InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks; PP.addPPCallbacks(Callbacks); // Takes ownership. // 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->FilenameRange; } }; 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 \n"; CharSourceRange Range = InclusionDirectiveFilenameRange(Source, "/angled.h", true); ASSERT_EQ("", 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 \n" "#include MACRO_ANGLED\n"; CharSourceRange Range = InclusionDirectiveFilenameRange(Source, "/angled.h", true); ASSERT_EQ("", 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 \n" "#define MACRO_CONCAT(x, y) x ## _ ## y\n" "#include MACRO_CONCAT(MACRO, ANGLED)\n"; CharSourceRange Range = InclusionDirectiveFilenameRange(Source, "/angled.h", false); ASSERT_EQ("", GetSourceString(Range)); } #pragma clang diagnostic ignored "-Wtrigraphs" 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)); } } // anonoymous namespace