1 //===- unittests/Lex/HeaderSearchTest.cpp ------ HeaderSearch tests -------===//
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
7 //===----------------------------------------------------------------------===//
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"
27 class HeaderSearchTest
: public ::testing::Test
{
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
);
40 void addSearchDir(llvm::StringRef Dir
) {
42 Dir
, 0, llvm::MemoryBuffer::getMemBuffer(""), /*User=*/std::nullopt
,
43 /*Group=*/std::nullopt
, llvm::sys::fs::file_type::directory_file
);
44 auto DE
= FileMgr
.getOptionalDirectoryRef(Dir
);
46 auto DL
= DirectoryLookup(*DE
, SrcMgr::C_User
, /*isFramework=*/false);
47 Search
.AddSearchPath(DL
, /*isAngled=*/false);
50 void addFrameworkSearchDir(llvm::StringRef Dir
, bool IsSystem
= true) {
52 Dir
, 0, llvm::MemoryBuffer::getMemBuffer(""), /*User=*/std::nullopt
,
53 /*Group=*/std::nullopt
, llvm::sys::fs::file_type::directory_file
);
54 auto DE
= FileMgr
.getOptionalDirectoryRef(Dir
);
56 auto DL
= DirectoryLookup(*DE
, IsSystem
? SrcMgr::C_System
: SrcMgr::C_User
,
57 /*isFramework=*/true);
59 Search
.AddSystemSearchPath(DL
);
61 Search
.AddSearchPath(DL
, /*isAngled=*/true);
64 void addHeaderMap(llvm::StringRef Filename
,
65 std::unique_ptr
<llvm::MemoryBuffer
> Buf
,
66 bool isAngled
= false) {
67 VFS
->addFile(Filename
, 0, std::move(Buf
), /*User=*/std::nullopt
,
68 /*Group=*/std::nullopt
,
69 llvm::sys::fs::file_type::regular_file
);
70 auto FE
= FileMgr
.getOptionalFileRef(Filename
, true);
73 // Test class supports only one HMap at a time.
75 HMap
= HeaderMap::Create(*FE
, FileMgr
);
77 DirectoryLookup(HMap
.get(), SrcMgr::C_User
, /*isFramework=*/false);
78 Search
.AddSearchPath(DL
, isAngled
);
81 IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> VFS
;
82 FileSystemOptions FileMgrOpts
;
84 IntrusiveRefCntPtr
<DiagnosticIDs
> DiagID
;
85 DiagnosticsEngine Diags
;
86 SourceManager SourceMgr
;
88 std::shared_ptr
<TargetOptions
> TargetOpts
;
89 IntrusiveRefCntPtr
<TargetInfo
> Target
;
91 std::unique_ptr
<HeaderMap
> HMap
;
94 TEST_F(HeaderSearchTest
, NoSearchDir
) {
95 EXPECT_EQ(Search
.search_dir_size(), 0u);
96 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"",
101 TEST_F(HeaderSearchTest
, SimpleShorten
) {
103 addSearchDir("/x/y");
104 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"",
107 addSearchDir("/a/b/");
108 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/a/b/c", /*WorkingDir=*/"",
113 TEST_F(HeaderSearchTest
, ShortenWithWorkingDir
) {
115 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/a/b/c/x/y/z",
116 /*WorkingDir=*/"/a/b/c",
121 TEST_F(HeaderSearchTest
, Dots
) {
122 addSearchDir("/x/./y/");
123 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/x/y/./z",
127 addSearchDir("a/.././c/");
128 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/m/n/./c/z",
129 /*WorkingDir=*/"/m/n/",
135 TEST_F(HeaderSearchTest
, BackSlash
) {
136 addSearchDir("C:\\x\\y\\");
137 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t",
143 TEST_F(HeaderSearchTest
, BackSlashWithDotDot
) {
144 addSearchDir("..\\y");
145 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t",
146 /*WorkingDir=*/"C:/x/y/",
152 TEST_F(HeaderSearchTest
, DotDotsWithAbsPath
) {
153 addSearchDir("/x/../y/");
154 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/y/z",
160 TEST_F(HeaderSearchTest
, BothDotDots
) {
161 addSearchDir("/x/../y/");
162 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/x/../y/z",
168 TEST_F(HeaderSearchTest
, IncludeFromSameDirectory
) {
169 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/y/z/t.h",
171 /*MainFile=*/"/y/a.cc"),
175 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/y/z/t.h",
177 /*MainFile=*/"/y/a.cc"),
181 TEST_F(HeaderSearchTest
, SdkFramework
) {
182 addFrameworkSearchDir(
183 "/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/Frameworks/");
184 bool IsAngled
= false;
185 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics(
186 "/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/"
187 "Frameworks/AppKit.framework/Headers/NSView.h",
189 /*MainFile=*/"", &IsAngled
),
191 EXPECT_TRUE(IsAngled
);
193 addFrameworkSearchDir("/System/Developer/Library/Framworks/",
195 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics(
196 "/System/Developer/Library/Framworks/"
197 "Foo.framework/Headers/Foo.h",
199 /*MainFile=*/"", &IsAngled
),
201 // Expect to be true even though we passed false to IsSystem earlier since
202 // all frameworks should be treated as <>.
203 EXPECT_TRUE(IsAngled
);
206 TEST_F(HeaderSearchTest
, NestedFramework
) {
207 addFrameworkSearchDir("/Platforms/MacOSX/Frameworks");
208 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics(
209 "/Platforms/MacOSX/Frameworks/AppKit.framework/Frameworks/"
210 "Sub.framework/Headers/Sub.h",
216 TEST_F(HeaderSearchTest
, HeaderFrameworkLookup
) {
217 std::string HeaderPath
= "/tmp/Frameworks/Foo.framework/Headers/Foo.h";
218 addFrameworkSearchDir("/tmp/Frameworks");
219 VFS
->addFile(HeaderPath
, 0,
220 llvm::MemoryBuffer::getMemBufferCopy("", HeaderPath
),
221 /*User=*/std::nullopt
, /*Group=*/std::nullopt
,
222 llvm::sys::fs::file_type::regular_file
);
224 bool IsFrameworkFound
= false;
225 auto FoundFile
= Search
.LookupFile(
226 "Foo/Foo.h", SourceLocation(), /*isAngled=*/true, /*FromDir=*/nullptr,
227 /*CurDir=*/nullptr, /*Includers=*/{}, /*SearchPath=*/nullptr,
228 /*RelativePath=*/nullptr, /*RequestingModule=*/nullptr,
229 /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr, &IsFrameworkFound
);
231 EXPECT_TRUE(FoundFile
.has_value());
232 EXPECT_TRUE(IsFrameworkFound
);
233 auto &FE
= *FoundFile
;
234 auto FI
= Search
.getExistingFileInfo(FE
);
236 EXPECT_TRUE(FI
->IsValid
);
237 EXPECT_EQ(FI
->Framework
.str(), "Foo");
238 EXPECT_EQ(Search
.getIncludeNameForHeader(FE
), "Foo/Foo.h");
241 // Helper struct with null terminator character to make MemoryBuffer happy.
242 template <class FileTy
, class PaddingTy
>
243 struct NullTerminatedFile
: public FileTy
{
244 PaddingTy Padding
= 0;
247 TEST_F(HeaderSearchTest
, HeaderMapReverseLookup
) {
248 typedef NullTerminatedFile
<test::HMapFileMock
<2, 32>, char> FileTy
;
252 test::HMapFileMockMaker
<FileTy
> Maker(File
);
253 auto a
= Maker
.addString("d.h");
254 auto b
= Maker
.addString("b/");
255 auto c
= Maker
.addString("c.h");
256 Maker
.addBucket("d.h", a
, b
, c
);
258 addHeaderMap("/x/y/z.hmap", File
.getBuffer());
261 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/a/b/c.h",
267 TEST_F(HeaderSearchTest
, HeaderMapFrameworkLookup
) {
268 typedef NullTerminatedFile
<test::HMapFileMock
<4, 128>, char> FileTy
;
272 std::string HeaderDirName
= "/tmp/Sources/Foo/Headers/";
273 std::string HeaderName
= "Foo.h";
274 if (is_style_windows(llvm::sys::path::Style::native
)) {
275 // Force header path to be absolute on windows.
276 // As headermap content should represent absolute locations.
277 HeaderDirName
= "C:" + HeaderDirName
;
280 test::HMapFileMockMaker
<FileTy
> Maker(File
);
281 auto a
= Maker
.addString("Foo/Foo.h");
282 auto b
= Maker
.addString(HeaderDirName
);
283 auto c
= Maker
.addString(HeaderName
);
284 Maker
.addBucket("Foo/Foo.h", a
, b
, c
);
285 addHeaderMap("product-headers.hmap", File
.getBuffer(), /*isAngled=*/true);
288 HeaderDirName
+ HeaderName
, 0,
289 llvm::MemoryBuffer::getMemBufferCopy("", HeaderDirName
+ HeaderName
),
290 /*User=*/std::nullopt
, /*Group=*/std::nullopt
,
291 llvm::sys::fs::file_type::regular_file
);
293 bool IsMapped
= false;
294 auto FoundFile
= Search
.LookupFile(
295 "Foo/Foo.h", SourceLocation(), /*isAngled=*/true, /*FromDir=*/nullptr,
296 /*CurDir=*/nullptr, /*Includers=*/{}, /*SearchPath=*/nullptr,
297 /*RelativePath=*/nullptr, /*RequestingModule=*/nullptr,
298 /*SuggestedModule=*/nullptr, &IsMapped
,
299 /*IsFrameworkFound=*/nullptr);
301 EXPECT_TRUE(FoundFile
.has_value());
302 EXPECT_TRUE(IsMapped
);
303 auto &FE
= *FoundFile
;
304 auto FI
= Search
.getExistingFileInfo(FE
);
306 EXPECT_TRUE(FI
->IsValid
);
307 EXPECT_EQ(FI
->Framework
.str(), "Foo");
308 EXPECT_EQ(Search
.getIncludeNameForHeader(FE
), "Foo/Foo.h");