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:
HeaderSearchTest()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 
addSearchDir(llvm::StringRef Dir)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 
addSystemFrameworkSearchDir(llvm::StringRef Dir)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 
addHeaderMap(llvm::StringRef Filename,std::unique_ptr<llvm::MemoryBuffer> Buf,bool isAngled=false)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 
TEST_F(HeaderSearchTest,NoSearchDir)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 
TEST_F(HeaderSearchTest,SimpleShorten)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 
TEST_F(HeaderSearchTest,ShortenWithWorkingDir)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 
TEST_F(HeaderSearchTest,Dots)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
TEST_F(HeaderSearchTest,BackSlash)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 
TEST_F(HeaderSearchTest,BackSlashWithDotDot)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 
TEST_F(HeaderSearchTest,DotDotsWithAbsPath)145 TEST_F(HeaderSearchTest, DotDotsWithAbsPath) {
146   addSearchDir("/x/../y/");
147   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z",
148                                                    /*WorkingDir=*/"",
149                                                    /*MainFile=*/""),
150             "z");
151 }
152 
TEST_F(HeaderSearchTest,IncludeFromSameDirectory)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 
TEST_F(HeaderSearchTest,SdkFramework)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 
TEST_F(HeaderSearchTest,NestedFramework)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 
TEST_F(HeaderSearchTest,HeaderFrameworkLookup)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.has_value());
204   EXPECT_TRUE(IsFrameworkFound);
205   auto &FE = FoundFile.value();
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 
TEST_F(HeaderSearchTest,HeaderMapReverseLookup)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 
TEST_F(HeaderSearchTest,HeaderMapFrameworkLookup)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.has_value());
273   EXPECT_TRUE(IsMapped);
274   auto &FE = FoundFile.value();
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