1 //===- unittests/Lex/HeaderSearchTest.cpp ------ HeaderSearch tests -------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Lex/HeaderSearch.h" 10 #include "HeaderMapTestUtils.h" 11 #include "clang/Basic/Diagnostic.h" 12 #include "clang/Basic/DiagnosticOptions.h" 13 #include "clang/Basic/FileManager.h" 14 #include "clang/Basic/LangOptions.h" 15 #include "clang/Basic/SourceManager.h" 16 #include "clang/Basic/TargetInfo.h" 17 #include "clang/Basic/TargetOptions.h" 18 #include "clang/Lex/HeaderSearch.h" 19 #include "clang/Lex/HeaderSearchOptions.h" 20 #include "clang/Serialization/InMemoryModuleCache.h" 21 #include "gtest/gtest.h" 22 23 namespace clang { 24 namespace { 25 26 // The test fixture. 27 class HeaderSearchTest : public ::testing::Test { 28 protected: 29 HeaderSearchTest() 30 : VFS(new llvm::vfs::InMemoryFileSystem), FileMgr(FileMgrOpts, VFS), 31 DiagID(new DiagnosticIDs()), 32 Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()), 33 SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions), 34 Search(std::make_shared<HeaderSearchOptions>(), SourceMgr, Diags, 35 LangOpts, Target.get()) { 36 TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; 37 Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); 38 } 39 40 void addSearchDir(llvm::StringRef Dir) { 41 VFS->addFile(Dir, 0, llvm::MemoryBuffer::getMemBuffer(""), /*User=*/None, 42 /*Group=*/None, llvm::sys::fs::file_type::directory_file); 43 auto DE = FileMgr.getOptionalDirectoryRef(Dir); 44 assert(DE); 45 auto DL = DirectoryLookup(*DE, SrcMgr::C_User, /*isFramework=*/false); 46 Search.AddSearchPath(DL, /*isAngled=*/false); 47 } 48 49 void addHeaderMap(llvm::StringRef Filename, 50 std::unique_ptr<llvm::MemoryBuffer> Buf) { 51 VFS->addFile(Filename, 0, std::move(Buf), /*User=*/None, /*Group=*/None, 52 llvm::sys::fs::file_type::regular_file); 53 auto FE = FileMgr.getFile(Filename, true); 54 assert(FE); 55 56 // Test class supports only one HMap at a time. 57 assert(!HMap); 58 HMap = HeaderMap::Create(*FE, FileMgr); 59 auto DL = 60 DirectoryLookup(HMap.get(), SrcMgr::C_User, /*isFramework=*/false); 61 Search.AddSearchPath(DL, /*isAngled=*/false); 62 } 63 64 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS; 65 FileSystemOptions FileMgrOpts; 66 FileManager FileMgr; 67 IntrusiveRefCntPtr<DiagnosticIDs> DiagID; 68 DiagnosticsEngine Diags; 69 SourceManager SourceMgr; 70 LangOptions LangOpts; 71 std::shared_ptr<TargetOptions> TargetOpts; 72 IntrusiveRefCntPtr<TargetInfo> Target; 73 HeaderSearch Search; 74 std::unique_ptr<HeaderMap> HMap; 75 }; 76 77 TEST_F(HeaderSearchTest, NoSearchDir) { 78 EXPECT_EQ(Search.search_dir_size(), 0u); 79 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"", 80 /*MainFile=*/""), 81 "/x/y/z"); 82 } 83 84 TEST_F(HeaderSearchTest, SimpleShorten) { 85 addSearchDir("/x"); 86 addSearchDir("/x/y"); 87 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"", 88 /*MainFile=*/""), 89 "z"); 90 addSearchDir("/a/b/"); 91 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c", /*WorkingDir=*/"", 92 /*MainFile=*/""), 93 "c"); 94 } 95 96 TEST_F(HeaderSearchTest, ShortenWithWorkingDir) { 97 addSearchDir("x/y"); 98 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c/x/y/z", 99 /*WorkingDir=*/"/a/b/c", 100 /*MainFile=*/""), 101 "z"); 102 } 103 104 TEST_F(HeaderSearchTest, Dots) { 105 addSearchDir("/x/./y/"); 106 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/./z", 107 /*WorkingDir=*/"", 108 /*MainFile=*/""), 109 "z"); 110 addSearchDir("a/.././c/"); 111 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/m/n/./c/z", 112 /*WorkingDir=*/"/m/n/", 113 /*MainFile=*/""), 114 "z"); 115 } 116 117 #ifdef _WIN32 118 TEST_F(HeaderSearchTest, BackSlash) { 119 addSearchDir("C:\\x\\y\\"); 120 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t", 121 /*WorkingDir=*/"", 122 /*MainFile=*/""), 123 "z/t"); 124 } 125 126 TEST_F(HeaderSearchTest, BackSlashWithDotDot) { 127 addSearchDir("..\\y"); 128 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t", 129 /*WorkingDir=*/"C:/x/y/", 130 /*MainFile=*/""), 131 "z/t"); 132 } 133 #endif 134 135 TEST_F(HeaderSearchTest, DotDotsWithAbsPath) { 136 addSearchDir("/x/../y/"); 137 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z", 138 /*WorkingDir=*/"", 139 /*MainFile=*/""), 140 "z"); 141 } 142 143 TEST_F(HeaderSearchTest, IncludeFromSameDirectory) { 144 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z/t.h", 145 /*WorkingDir=*/"", 146 /*MainFile=*/"/y/a.cc"), 147 "z/t.h"); 148 149 addSearchDir("/"); 150 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z/t.h", 151 /*WorkingDir=*/"", 152 /*MainFile=*/"/y/a.cc"), 153 "y/z/t.h"); 154 } 155 156 // Helper struct with null terminator character to make MemoryBuffer happy. 157 template <class FileTy, class PaddingTy> 158 struct NullTerminatedFile : public FileTy { 159 PaddingTy Padding = 0; 160 }; 161 162 TEST_F(HeaderSearchTest, HeaderMapReverseLookup) { 163 typedef NullTerminatedFile<test::HMapFileMock<2, 32>, char> FileTy; 164 FileTy File; 165 File.init(); 166 167 test::HMapFileMockMaker<FileTy> Maker(File); 168 auto a = Maker.addString("d.h"); 169 auto b = Maker.addString("b/"); 170 auto c = Maker.addString("c.h"); 171 Maker.addBucket("d.h", a, b, c); 172 173 addHeaderMap("/x/y/z.hmap", File.getBuffer()); 174 addSearchDir("/a"); 175 176 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c.h", 177 /*WorkingDir=*/"", 178 /*MainFile=*/""), 179 "d.h"); 180 } 181 182 } // namespace 183 } // namespace clang 184