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