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/HeaderSearchOptions.h" 19 #include "clang/Serialization/InMemoryModuleCache.h" 20 #include "llvm/Support/MemoryBuffer.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 addSystemFrameworkSearchDir(llvm::StringRef Dir) { 50 VFS->addFile(Dir, 0, llvm::MemoryBuffer::getMemBuffer(""), /*User=*/None, 51 /*Group=*/None, llvm::sys::fs::file_type::directory_file); 52 auto DE = FileMgr.getOptionalDirectoryRef(Dir); 53 assert(DE); 54 auto DL = DirectoryLookup(*DE, SrcMgr::C_System, /*isFramework=*/true); 55 Search.AddSystemSearchPath(DL); 56 } 57 58 void addHeaderMap(llvm::StringRef Filename, 59 std::unique_ptr<llvm::MemoryBuffer> Buf, 60 bool isAngled = false) { 61 VFS->addFile(Filename, 0, std::move(Buf), /*User=*/None, /*Group=*/None, 62 llvm::sys::fs::file_type::regular_file); 63 auto FE = FileMgr.getFile(Filename, true); 64 assert(FE); 65 66 // Test class supports only one HMap at a time. 67 assert(!HMap); 68 HMap = HeaderMap::Create(*FE, FileMgr); 69 auto DL = 70 DirectoryLookup(HMap.get(), SrcMgr::C_User, /*isFramework=*/false); 71 Search.AddSearchPath(DL, isAngled); 72 } 73 74 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS; 75 FileSystemOptions FileMgrOpts; 76 FileManager FileMgr; 77 IntrusiveRefCntPtr<DiagnosticIDs> DiagID; 78 DiagnosticsEngine Diags; 79 SourceManager SourceMgr; 80 LangOptions LangOpts; 81 std::shared_ptr<TargetOptions> TargetOpts; 82 IntrusiveRefCntPtr<TargetInfo> Target; 83 HeaderSearch Search; 84 std::unique_ptr<HeaderMap> HMap; 85 }; 86 87 TEST_F(HeaderSearchTest, NoSearchDir) { 88 EXPECT_EQ(Search.search_dir_size(), 0u); 89 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"", 90 /*MainFile=*/""), 91 "/x/y/z"); 92 } 93 94 TEST_F(HeaderSearchTest, SimpleShorten) { 95 addSearchDir("/x"); 96 addSearchDir("/x/y"); 97 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"", 98 /*MainFile=*/""), 99 "z"); 100 addSearchDir("/a/b/"); 101 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c", /*WorkingDir=*/"", 102 /*MainFile=*/""), 103 "c"); 104 } 105 106 TEST_F(HeaderSearchTest, ShortenWithWorkingDir) { 107 addSearchDir("x/y"); 108 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c/x/y/z", 109 /*WorkingDir=*/"/a/b/c", 110 /*MainFile=*/""), 111 "z"); 112 } 113 114 TEST_F(HeaderSearchTest, Dots) { 115 addSearchDir("/x/./y/"); 116 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/./z", 117 /*WorkingDir=*/"", 118 /*MainFile=*/""), 119 "z"); 120 addSearchDir("a/.././c/"); 121 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/m/n/./c/z", 122 /*WorkingDir=*/"/m/n/", 123 /*MainFile=*/""), 124 "z"); 125 } 126 127 #ifdef _WIN32 128 TEST_F(HeaderSearchTest, BackSlash) { 129 addSearchDir("C:\\x\\y\\"); 130 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t", 131 /*WorkingDir=*/"", 132 /*MainFile=*/""), 133 "z/t"); 134 } 135 136 TEST_F(HeaderSearchTest, BackSlashWithDotDot) { 137 addSearchDir("..\\y"); 138 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t", 139 /*WorkingDir=*/"C:/x/y/", 140 /*MainFile=*/""), 141 "z/t"); 142 } 143 #endif 144 145 TEST_F(HeaderSearchTest, DotDotsWithAbsPath) { 146 addSearchDir("/x/../y/"); 147 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z", 148 /*WorkingDir=*/"", 149 /*MainFile=*/""), 150 "z"); 151 } 152 153 TEST_F(HeaderSearchTest, IncludeFromSameDirectory) { 154 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z/t.h", 155 /*WorkingDir=*/"", 156 /*MainFile=*/"/y/a.cc"), 157 "z/t.h"); 158 159 addSearchDir("/"); 160 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z/t.h", 161 /*WorkingDir=*/"", 162 /*MainFile=*/"/y/a.cc"), 163 "y/z/t.h"); 164 } 165 166 TEST_F(HeaderSearchTest, SdkFramework) { 167 addSystemFrameworkSearchDir( 168 "/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/Frameworks/"); 169 bool IsSystem = false; 170 EXPECT_EQ(Search.suggestPathToFileForDiagnostics( 171 "/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/" 172 "Frameworks/AppKit.framework/Headers/NSView.h", 173 /*WorkingDir=*/"", 174 /*MainFile=*/"", &IsSystem), 175 "AppKit/NSView.h"); 176 EXPECT_TRUE(IsSystem); 177 } 178 179 TEST_F(HeaderSearchTest, NestedFramework) { 180 addSystemFrameworkSearchDir("/Platforms/MacOSX/Frameworks"); 181 EXPECT_EQ(Search.suggestPathToFileForDiagnostics( 182 "/Platforms/MacOSX/Frameworks/AppKit.framework/Frameworks/" 183 "Sub.framework/Headers/Sub.h", 184 /*WorkingDir=*/"", 185 /*MainFile=*/""), 186 "Sub/Sub.h"); 187 } 188 189 TEST_F(HeaderSearchTest, HeaderFrameworkLookup) { 190 std::string HeaderPath = "/tmp/Frameworks/Foo.framework/Headers/Foo.h"; 191 addSystemFrameworkSearchDir("/tmp/Frameworks"); 192 VFS->addFile( 193 HeaderPath, 0, llvm::MemoryBuffer::getMemBufferCopy("", HeaderPath), 194 /*User=*/None, /*Group=*/None, llvm::sys::fs::file_type::regular_file); 195 196 bool IsFrameworkFound = false; 197 auto FoundFile = Search.LookupFile( 198 "Foo/Foo.h", SourceLocation(), /*isAngled=*/true, /*FromDir=*/nullptr, 199 /*CurDir=*/nullptr, /*Includers=*/{}, /*SearchPath=*/nullptr, 200 /*RelativePath=*/nullptr, /*RequestingModule=*/nullptr, 201 /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr, &IsFrameworkFound); 202 203 EXPECT_TRUE(FoundFile.hasValue()); 204 EXPECT_TRUE(IsFrameworkFound); 205 auto &FE = FoundFile.getValue(); 206 auto FI = Search.getExistingFileInfo(FE); 207 EXPECT_TRUE(FI); 208 EXPECT_TRUE(FI->IsValid); 209 EXPECT_EQ(FI->Framework.str(), "Foo"); 210 EXPECT_EQ(Search.getIncludeNameForHeader(FE), "Foo/Foo.h"); 211 } 212 213 // Helper struct with null terminator character to make MemoryBuffer happy. 214 template <class FileTy, class PaddingTy> 215 struct NullTerminatedFile : public FileTy { 216 PaddingTy Padding = 0; 217 }; 218 219 TEST_F(HeaderSearchTest, HeaderMapReverseLookup) { 220 typedef NullTerminatedFile<test::HMapFileMock<2, 32>, char> FileTy; 221 FileTy File; 222 File.init(); 223 224 test::HMapFileMockMaker<FileTy> Maker(File); 225 auto a = Maker.addString("d.h"); 226 auto b = Maker.addString("b/"); 227 auto c = Maker.addString("c.h"); 228 Maker.addBucket("d.h", a, b, c); 229 230 addHeaderMap("/x/y/z.hmap", File.getBuffer()); 231 addSearchDir("/a"); 232 233 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c.h", 234 /*WorkingDir=*/"", 235 /*MainFile=*/""), 236 "d.h"); 237 } 238 239 TEST_F(HeaderSearchTest, HeaderMapFrameworkLookup) { 240 typedef NullTerminatedFile<test::HMapFileMock<4, 128>, char> FileTy; 241 FileTy File; 242 File.init(); 243 244 std::string HeaderDirName = "/tmp/Sources/Foo/Headers/"; 245 std::string HeaderName = "Foo.h"; 246 if (is_style_windows(llvm::sys::path::Style::native)) { 247 // Force header path to be absolute on windows. 248 // As headermap content should represent absolute locations. 249 HeaderDirName = "C:" + HeaderDirName; 250 } 251 252 test::HMapFileMockMaker<FileTy> Maker(File); 253 auto a = Maker.addString("Foo/Foo.h"); 254 auto b = Maker.addString(HeaderDirName); 255 auto c = Maker.addString(HeaderName); 256 Maker.addBucket("Foo/Foo.h", a, b, c); 257 addHeaderMap("product-headers.hmap", File.getBuffer(), /*isAngled=*/true); 258 259 VFS->addFile( 260 HeaderDirName + HeaderName, 0, 261 llvm::MemoryBuffer::getMemBufferCopy("", HeaderDirName + HeaderName), 262 /*User=*/None, /*Group=*/None, llvm::sys::fs::file_type::regular_file); 263 264 bool IsMapped = false; 265 auto FoundFile = Search.LookupFile( 266 "Foo/Foo.h", SourceLocation(), /*isAngled=*/true, /*FromDir=*/nullptr, 267 /*CurDir=*/nullptr, /*Includers=*/{}, /*SearchPath=*/nullptr, 268 /*RelativePath=*/nullptr, /*RequestingModule=*/nullptr, 269 /*SuggestedModule=*/nullptr, &IsMapped, 270 /*IsFrameworkFound=*/nullptr); 271 272 EXPECT_TRUE(FoundFile.hasValue()); 273 EXPECT_TRUE(IsMapped); 274 auto &FE = FoundFile.getValue(); 275 auto FI = Search.getExistingFileInfo(FE); 276 EXPECT_TRUE(FI); 277 EXPECT_TRUE(FI->IsValid); 278 EXPECT_EQ(FI->Framework.str(), "Foo"); 279 EXPECT_EQ(Search.getIncludeNameForHeader(FE), "Foo/Foo.h"); 280 } 281 282 } // namespace 283 } // namespace clang 284