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 "llvm/Support/MemoryBuffer.h" 22 #include "gtest/gtest.h" 23 24 namespace clang { 25 namespace { 26 27 // The test fixture. 28 class HeaderSearchTest : public ::testing::Test { 29 protected: 30 HeaderSearchTest() 31 : VFS(new llvm::vfs::InMemoryFileSystem), FileMgr(FileMgrOpts, VFS), 32 DiagID(new DiagnosticIDs()), 33 Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()), 34 SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions), 35 Search(std::make_shared<HeaderSearchOptions>(), SourceMgr, Diags, 36 LangOpts, Target.get()) { 37 TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; 38 Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); 39 } 40 41 void addSearchDir(llvm::StringRef Dir) { 42 VFS->addFile(Dir, 0, llvm::MemoryBuffer::getMemBuffer(""), /*User=*/None, 43 /*Group=*/None, llvm::sys::fs::file_type::directory_file); 44 auto DE = FileMgr.getOptionalDirectoryRef(Dir); 45 assert(DE); 46 auto DL = DirectoryLookup(*DE, SrcMgr::C_User, /*isFramework=*/false); 47 Search.AddSearchPath(DL, /*isAngled=*/false); 48 } 49 50 void addHeaderMap(llvm::StringRef Filename, 51 std::unique_ptr<llvm::MemoryBuffer> Buf, 52 bool isAngled = false) { 53 VFS->addFile(Filename, 0, std::move(Buf), /*User=*/None, /*Group=*/None, 54 llvm::sys::fs::file_type::regular_file); 55 auto FE = FileMgr.getFile(Filename, true); 56 assert(FE); 57 58 // Test class supports only one HMap at a time. 59 assert(!HMap); 60 HMap = HeaderMap::Create(*FE, FileMgr); 61 auto DL = 62 DirectoryLookup(HMap.get(), SrcMgr::C_User, /*isFramework=*/false); 63 Search.AddSearchPath(DL, isAngled); 64 } 65 66 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS; 67 FileSystemOptions FileMgrOpts; 68 FileManager FileMgr; 69 IntrusiveRefCntPtr<DiagnosticIDs> DiagID; 70 DiagnosticsEngine Diags; 71 SourceManager SourceMgr; 72 LangOptions LangOpts; 73 std::shared_ptr<TargetOptions> TargetOpts; 74 IntrusiveRefCntPtr<TargetInfo> Target; 75 HeaderSearch Search; 76 std::unique_ptr<HeaderMap> HMap; 77 }; 78 79 TEST_F(HeaderSearchTest, NoSearchDir) { 80 EXPECT_EQ(Search.search_dir_size(), 0u); 81 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"", 82 /*MainFile=*/""), 83 "/x/y/z"); 84 } 85 86 TEST_F(HeaderSearchTest, SimpleShorten) { 87 addSearchDir("/x"); 88 addSearchDir("/x/y"); 89 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"", 90 /*MainFile=*/""), 91 "z"); 92 addSearchDir("/a/b/"); 93 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c", /*WorkingDir=*/"", 94 /*MainFile=*/""), 95 "c"); 96 } 97 98 TEST_F(HeaderSearchTest, ShortenWithWorkingDir) { 99 addSearchDir("x/y"); 100 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c/x/y/z", 101 /*WorkingDir=*/"/a/b/c", 102 /*MainFile=*/""), 103 "z"); 104 } 105 106 TEST_F(HeaderSearchTest, Dots) { 107 addSearchDir("/x/./y/"); 108 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/./z", 109 /*WorkingDir=*/"", 110 /*MainFile=*/""), 111 "z"); 112 addSearchDir("a/.././c/"); 113 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/m/n/./c/z", 114 /*WorkingDir=*/"/m/n/", 115 /*MainFile=*/""), 116 "z"); 117 } 118 119 #ifdef _WIN32 120 TEST_F(HeaderSearchTest, BackSlash) { 121 addSearchDir("C:\\x\\y\\"); 122 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t", 123 /*WorkingDir=*/"", 124 /*MainFile=*/""), 125 "z/t"); 126 } 127 128 TEST_F(HeaderSearchTest, BackSlashWithDotDot) { 129 addSearchDir("..\\y"); 130 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t", 131 /*WorkingDir=*/"C:/x/y/", 132 /*MainFile=*/""), 133 "z/t"); 134 } 135 #endif 136 137 TEST_F(HeaderSearchTest, DotDotsWithAbsPath) { 138 addSearchDir("/x/../y/"); 139 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z", 140 /*WorkingDir=*/"", 141 /*MainFile=*/""), 142 "z"); 143 } 144 145 TEST_F(HeaderSearchTest, IncludeFromSameDirectory) { 146 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z/t.h", 147 /*WorkingDir=*/"", 148 /*MainFile=*/"/y/a.cc"), 149 "z/t.h"); 150 151 addSearchDir("/"); 152 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z/t.h", 153 /*WorkingDir=*/"", 154 /*MainFile=*/"/y/a.cc"), 155 "y/z/t.h"); 156 } 157 158 // Helper struct with null terminator character to make MemoryBuffer happy. 159 template <class FileTy, class PaddingTy> 160 struct NullTerminatedFile : public FileTy { 161 PaddingTy Padding = 0; 162 }; 163 164 TEST_F(HeaderSearchTest, HeaderMapReverseLookup) { 165 typedef NullTerminatedFile<test::HMapFileMock<2, 32>, char> FileTy; 166 FileTy File; 167 File.init(); 168 169 test::HMapFileMockMaker<FileTy> Maker(File); 170 auto a = Maker.addString("d.h"); 171 auto b = Maker.addString("b/"); 172 auto c = Maker.addString("c.h"); 173 Maker.addBucket("d.h", a, b, c); 174 175 addHeaderMap("/x/y/z.hmap", File.getBuffer()); 176 addSearchDir("/a"); 177 178 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c.h", 179 /*WorkingDir=*/"", 180 /*MainFile=*/""), 181 "d.h"); 182 } 183 184 TEST_F(HeaderSearchTest, HeaderMapFrameworkLookup) { 185 typedef NullTerminatedFile<test::HMapFileMock<4, 128>, char> FileTy; 186 FileTy File; 187 File.init(); 188 189 std::string HeaderDirName = "/tmp/Sources/Foo/Headers/"; 190 std::string HeaderName = "Foo.h"; 191 #ifdef _WIN32 192 // Force header path to be absolute on windows. 193 // As headermap content should represent absolute locations. 194 HeaderDirName = "C:" + HeaderDirName; 195 #endif /*_WIN32*/ 196 197 test::HMapFileMockMaker<FileTy> Maker(File); 198 auto a = Maker.addString("Foo/Foo.h"); 199 auto b = Maker.addString(HeaderDirName); 200 auto c = Maker.addString(HeaderName); 201 Maker.addBucket("Foo/Foo.h", a, b, c); 202 addHeaderMap("product-headers.hmap", File.getBuffer(), /*isAngled=*/true); 203 204 VFS->addFile( 205 HeaderDirName + HeaderName, 0, 206 llvm::MemoryBuffer::getMemBufferCopy("", HeaderDirName + HeaderName), 207 /*User=*/None, /*Group=*/None, llvm::sys::fs::file_type::regular_file); 208 209 bool IsMapped = false; 210 const DirectoryLookup *CurDir = nullptr; 211 auto FoundFile = Search.LookupFile( 212 "Foo/Foo.h", SourceLocation(), /*isAngled=*/true, /*FromDir=*/nullptr, 213 CurDir, /*Includers=*/{}, /*SearchPath=*/nullptr, 214 /*RelativePath=*/nullptr, /*RequestingModule=*/nullptr, 215 /*SuggestedModule=*/nullptr, &IsMapped, 216 /*IsFrameworkFound=*/nullptr); 217 218 EXPECT_TRUE(FoundFile.hasValue()); 219 EXPECT_TRUE(IsMapped); 220 auto &FE = FoundFile.getValue(); 221 auto FI = Search.getExistingFileInfo(FE); 222 EXPECT_TRUE(FI); 223 EXPECT_TRUE(FI->IsValid); 224 EXPECT_EQ(FI->Framework.str(), "Foo"); 225 } 226 227 } // namespace 228 } // namespace clang 229