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
) {
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
);
45 auto DL
= DirectoryLookup(*DE
, SrcMgr::C_User
, /*isFramework=*/false);
46 Search
.AddSearchPath(DL
, /*isAngled=*/false);
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
);
54 auto DL
= DirectoryLookup(*DE
, SrcMgr::C_System
, /*isFramework=*/true);
55 Search
.AddSystemSearchPath(DL
);
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);
66 // Test class supports only one HMap at a time.
68 HMap
= HeaderMap::Create(*FE
, FileMgr
);
70 DirectoryLookup(HMap
.get(), SrcMgr::C_User
, /*isFramework=*/false);
71 Search
.AddSearchPath(DL
, isAngled
);
74 IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> VFS
;
75 FileSystemOptions FileMgrOpts
;
77 IntrusiveRefCntPtr
<DiagnosticIDs
> DiagID
;
78 DiagnosticsEngine Diags
;
79 SourceManager SourceMgr
;
81 std::shared_ptr
<TargetOptions
> TargetOpts
;
82 IntrusiveRefCntPtr
<TargetInfo
> Target
;
84 std::unique_ptr
<HeaderMap
> HMap
;
87 TEST_F(HeaderSearchTest
, NoSearchDir
) {
88 EXPECT_EQ(Search
.search_dir_size(), 0u);
89 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"",
94 TEST_F(HeaderSearchTest
, SimpleShorten
) {
97 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"",
100 addSearchDir("/a/b/");
101 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/a/b/c", /*WorkingDir=*/"",
106 TEST_F(HeaderSearchTest
, ShortenWithWorkingDir
) {
108 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/a/b/c/x/y/z",
109 /*WorkingDir=*/"/a/b/c",
114 TEST_F(HeaderSearchTest
, Dots
) {
115 addSearchDir("/x/./y/");
116 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/x/y/./z",
120 addSearchDir("a/.././c/");
121 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/m/n/./c/z",
122 /*WorkingDir=*/"/m/n/",
128 TEST_F(HeaderSearchTest
, BackSlash
) {
129 addSearchDir("C:\\x\\y\\");
130 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t",
136 TEST_F(HeaderSearchTest
, BackSlashWithDotDot
) {
137 addSearchDir("..\\y");
138 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t",
139 /*WorkingDir=*/"C:/x/y/",
145 TEST_F(HeaderSearchTest
, DotDotsWithAbsPath
) {
146 addSearchDir("/x/../y/");
147 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/y/z",
153 TEST_F(HeaderSearchTest
, IncludeFromSameDirectory
) {
154 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/y/z/t.h",
156 /*MainFile=*/"/y/a.cc"),
160 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/y/z/t.h",
162 /*MainFile=*/"/y/a.cc"),
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",
174 /*MainFile=*/"", &IsSystem
),
176 EXPECT_TRUE(IsSystem
);
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",
189 TEST_F(HeaderSearchTest
, HeaderFrameworkLookup
) {
190 std::string HeaderPath
= "/tmp/Frameworks/Foo.framework/Headers/Foo.h";
191 addSystemFrameworkSearchDir("/tmp/Frameworks");
193 HeaderPath
, 0, llvm::MemoryBuffer::getMemBufferCopy("", HeaderPath
),
194 /*User=*/None
, /*Group=*/None
, llvm::sys::fs::file_type::regular_file
);
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
);
203 EXPECT_TRUE(FoundFile
.has_value());
204 EXPECT_TRUE(IsFrameworkFound
);
205 auto &FE
= FoundFile
.value();
206 auto FI
= Search
.getExistingFileInfo(FE
);
208 EXPECT_TRUE(FI
->IsValid
);
209 EXPECT_EQ(FI
->Framework
.str(), "Foo");
210 EXPECT_EQ(Search
.getIncludeNameForHeader(FE
), "Foo/Foo.h");
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;
219 TEST_F(HeaderSearchTest
, HeaderMapReverseLookup
) {
220 typedef NullTerminatedFile
<test::HMapFileMock
<2, 32>, char> FileTy
;
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
);
230 addHeaderMap("/x/y/z.hmap", File
.getBuffer());
233 EXPECT_EQ(Search
.suggestPathToFileForDiagnostics("/a/b/c.h",
239 TEST_F(HeaderSearchTest
, HeaderMapFrameworkLookup
) {
240 typedef NullTerminatedFile
<test::HMapFileMock
<4, 128>, char> FileTy
;
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
;
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);
260 HeaderDirName
+ HeaderName
, 0,
261 llvm::MemoryBuffer::getMemBufferCopy("", HeaderDirName
+ HeaderName
),
262 /*User=*/None
, /*Group=*/None
, llvm::sys::fs::file_type::regular_file
);
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);
272 EXPECT_TRUE(FoundFile
.has_value());
273 EXPECT_TRUE(IsMapped
);
274 auto &FE
= FoundFile
.value();
275 auto FI
= Search
.getExistingFileInfo(FE
);
277 EXPECT_TRUE(FI
->IsValid
);
278 EXPECT_EQ(FI
->Framework
.str(), "Foo");
279 EXPECT_EQ(Search
.getIncludeNameForHeader(FE
), "Foo/Foo.h");