| //===- 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/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/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 ModuleLoadResult loadModule(SourceLocation ImportLoc, |
| ModuleIdPath Path, |
| Module::NameVisibilityKind Visibility, |
| bool IsInclusionDirective) { |
| return ModuleLoadResult(); |
| } |
| |
| virtual void makeModuleVisible(Module *Mod, |
| Module::NameVisibilityKind Visibility, |
| SourceLocation ImportLoc) { } |
| }; |
| |
| // 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<DiagnosticIDs> DiagID; |
| IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; |
| DiagnosticsEngine Diags; |
| SourceManager SourceMgr; |
| LangOptions LangOpts; |
| IntrusiveRefCntPtr<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. |
| 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, 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<HeaderSearchOptions> HSOpts = new HeaderSearchOptions(); |
| HeaderSearch HeaderInfo(HSOpts, FileMgr, Diags, LangOpts, Target.getPtr()); |
| AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); |
| |
| IntrusiveRefCntPtr<PreprocessorOptions> 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 <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)); |
| } |
| |
| } // anonoymous namespace |