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 "llvm/Support/MemoryBuffer.h"
20#include "gtest/gtest.h"
21#include <memory>
22#include <string>
23
24namespace clang {
25namespace {
26
27// The test fixture.
28class HeaderSearchTest : public ::testing::Test {
29protected:
30 HeaderSearchTest()
31 : VFS(new llvm::vfs::InMemoryFileSystem), FileMgr(FileMgrOpts, VFS),
32 DiagID(new DiagnosticIDs()),
33 Diags(DiagID, DiagOpts, new IgnoringDiagConsumer()),
34 SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions),
35 Search(HSOpts, SourceMgr, Diags, LangOpts, Target.get()) {
36 TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
37 Target = TargetInfo::CreateTargetInfo(Diags, Opts&: *TargetOpts);
38 }
39
40 void addSearchDir(llvm::StringRef Dir) {
41 VFS->addFile(
42 Path: Dir, ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: ""), /*User=*/std::nullopt,
43 /*Group=*/std::nullopt, Type: llvm::sys::fs::file_type::directory_file);
44 auto DE = FileMgr.getOptionalDirectoryRef(DirName: Dir);
45 assert(DE);
46 auto DL = DirectoryLookup(*DE, SrcMgr::C_User, /*isFramework=*/false);
47 Search.AddSearchPath(dir: DL, /*isAngled=*/false);
48 }
49
50 void addFrameworkSearchDir(llvm::StringRef Dir, bool IsSystem = true) {
51 VFS->addFile(
52 Path: Dir, ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: ""), /*User=*/std::nullopt,
53 /*Group=*/std::nullopt, Type: llvm::sys::fs::file_type::directory_file);
54 auto DE = FileMgr.getOptionalDirectoryRef(DirName: Dir);
55 assert(DE);
56 auto DL = DirectoryLookup(*DE, IsSystem ? SrcMgr::C_System : SrcMgr::C_User,
57 /*isFramework=*/true);
58 if (IsSystem)
59 Search.AddSystemSearchPath(dir: DL);
60 else
61 Search.AddSearchPath(dir: DL, /*isAngled=*/true);
62 }
63
64 void addHeaderMap(llvm::StringRef Filename,
65 std::unique_ptr<llvm::MemoryBuffer> Buf,
66 bool isAngled = false) {
67 VFS->addFile(Path: Filename, ModificationTime: 0, Buffer: std::move(Buf), /*User=*/std::nullopt,
68 /*Group=*/std::nullopt,
69 Type: llvm::sys::fs::file_type::regular_file);
70 auto FE = FileMgr.getOptionalFileRef(Filename, OpenFile: true);
71 assert(FE);
72
73 // Test class supports only one HMap at a time.
74 assert(!HMap);
75 HMap = HeaderMap::Create(FE: *FE, FM&: FileMgr);
76 auto DL = DirectoryLookup(HMap.get(), SrcMgr::C_User);
77 Search.AddSearchPath(dir: DL, isAngled);
78 }
79
80 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS;
81 FileSystemOptions FileMgrOpts;
82 FileManager FileMgr;
83 IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
84 DiagnosticOptions DiagOpts;
85 DiagnosticsEngine Diags;
86 SourceManager SourceMgr;
87 LangOptions LangOpts;
88 std::shared_ptr<TargetOptions> TargetOpts;
89 IntrusiveRefCntPtr<TargetInfo> Target;
90 HeaderSearchOptions HSOpts;
91 HeaderSearch Search;
92 std::unique_ptr<HeaderMap> HMap;
93};
94
95TEST_F(HeaderSearchTest, NoSearchDir) {
96 EXPECT_EQ(Search.search_dir_size(), 0u);
97 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"",
98 /*MainFile=*/""),
99 "/x/y/z");
100}
101
102TEST_F(HeaderSearchTest, SimpleShorten) {
103 addSearchDir(Dir: "/x");
104 addSearchDir(Dir: "/x/y");
105 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"",
106 /*MainFile=*/""),
107 "z");
108 addSearchDir(Dir: "/a/b/");
109 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c", /*WorkingDir=*/"",
110 /*MainFile=*/""),
111 "c");
112}
113
114TEST_F(HeaderSearchTest, ShortenWithWorkingDir) {
115 addSearchDir(Dir: "x/y");
116 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c/x/y/z",
117 /*WorkingDir=*/"/a/b/c",
118 /*MainFile=*/""),
119 "z");
120}
121
122TEST_F(HeaderSearchTest, Dots) {
123 addSearchDir(Dir: "/x/./y/");
124 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/./z",
125 /*WorkingDir=*/"",
126 /*MainFile=*/""),
127 "z");
128 addSearchDir(Dir: "a/.././c/");
129 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/m/n/./c/z",
130 /*WorkingDir=*/"/m/n/",
131 /*MainFile=*/""),
132 "z");
133}
134
135TEST_F(HeaderSearchTest, RelativeDirs) {
136 ASSERT_FALSE(VFS->setCurrentWorkingDirectory("/root/some/dir"));
137 addSearchDir(Dir: "..");
138 EXPECT_EQ(
139 Search.suggestPathToFileForDiagnostics("/root/some/foo.h",
140 /*WorkingDir=*/"/root/some/dir",
141 /*MainFile=*/""),
142 "foo.h");
143 EXPECT_EQ(
144 Search.suggestPathToFileForDiagnostics("../foo.h",
145 /*WorkingDir=*/"/root/some/dir",
146 /*MainFile=*/""),
147 "foo.h");
148}
149
150#ifdef _WIN32
151TEST_F(HeaderSearchTest, BackSlash) {
152 addSearchDir("C:\\x\\y\\");
153 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t",
154 /*WorkingDir=*/"",
155 /*MainFile=*/""),
156 "z/t");
157}
158
159TEST_F(HeaderSearchTest, BackSlashWithDotDot) {
160 addSearchDir("..\\y");
161 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t",
162 /*WorkingDir=*/"C:/x/y/",
163 /*MainFile=*/""),
164 "z/t");
165}
166#endif
167
168TEST_F(HeaderSearchTest, DotDotsWithAbsPath) {
169 addSearchDir(Dir: "/x/../y/");
170 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z",
171 /*WorkingDir=*/"",
172 /*MainFile=*/""),
173 "z");
174}
175
176TEST_F(HeaderSearchTest, BothDotDots) {
177 addSearchDir(Dir: "/x/../y/");
178 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/../y/z",
179 /*WorkingDir=*/"",
180 /*MainFile=*/""),
181 "z");
182}
183
184TEST_F(HeaderSearchTest, IncludeFromSameDirectory) {
185 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z/t.h",
186 /*WorkingDir=*/"",
187 /*MainFile=*/"/y/a.cc"),
188 "z/t.h");
189
190 addSearchDir(Dir: "/");
191 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z/t.h",
192 /*WorkingDir=*/"",
193 /*MainFile=*/"/y/a.cc"),
194 "y/z/t.h");
195}
196
197TEST_F(HeaderSearchTest, SdkFramework) {
198 addFrameworkSearchDir(
199 Dir: "/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/Frameworks/");
200 bool IsAngled = false;
201 EXPECT_EQ(Search.suggestPathToFileForDiagnostics(
202 "/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/"
203 "Frameworks/AppKit.framework/Headers/NSView.h",
204 /*WorkingDir=*/"",
205 /*MainFile=*/"", &IsAngled),
206 "AppKit/NSView.h");
207 EXPECT_TRUE(IsAngled);
208
209 addFrameworkSearchDir(Dir: "/System/Developer/Library/Framworks/",
210 /*IsSystem*/ false);
211 EXPECT_EQ(Search.suggestPathToFileForDiagnostics(
212 "/System/Developer/Library/Framworks/"
213 "Foo.framework/Headers/Foo.h",
214 /*WorkingDir=*/"",
215 /*MainFile=*/"", &IsAngled),
216 "Foo/Foo.h");
217 // Expect to be true even though we passed false to IsSystem earlier since
218 // all frameworks should be treated as <>.
219 EXPECT_TRUE(IsAngled);
220}
221
222TEST_F(HeaderSearchTest, NestedFramework) {
223 addFrameworkSearchDir(Dir: "/Platforms/MacOSX/Frameworks");
224 EXPECT_EQ(Search.suggestPathToFileForDiagnostics(
225 "/Platforms/MacOSX/Frameworks/AppKit.framework/Frameworks/"
226 "Sub.framework/Headers/Sub.h",
227 /*WorkingDir=*/"",
228 /*MainFile=*/""),
229 "Sub/Sub.h");
230}
231
232TEST_F(HeaderSearchTest, HeaderFrameworkLookup) {
233 std::string HeaderPath = "/tmp/Frameworks/Foo.framework/Headers/Foo.h";
234 addFrameworkSearchDir(Dir: "/tmp/Frameworks");
235 VFS->addFile(Path: HeaderPath, ModificationTime: 0,
236 Buffer: llvm::MemoryBuffer::getMemBufferCopy(InputData: "", BufferName: HeaderPath),
237 /*User=*/std::nullopt, /*Group=*/std::nullopt,
238 Type: llvm::sys::fs::file_type::regular_file);
239
240 bool IsFrameworkFound = false;
241 auto FoundFile = Search.LookupFile(
242 Filename: "Foo/Foo.h", IncludeLoc: SourceLocation(), /*isAngled=*/true, /*FromDir=*/nullptr,
243 /*CurDir=*/nullptr, /*Includers=*/{}, /*SearchPath=*/nullptr,
244 /*RelativePath=*/nullptr, /*RequestingModule=*/nullptr,
245 /*SuggestedModule=*/nullptr, /*IsMapped=*/nullptr, IsFrameworkFound: &IsFrameworkFound);
246
247 EXPECT_TRUE(FoundFile.has_value());
248 EXPECT_TRUE(IsFrameworkFound);
249 auto &FE = *FoundFile;
250 auto FI = Search.getExistingFileInfo(FE);
251 EXPECT_TRUE(FI);
252 EXPECT_TRUE(FI->IsValid);
253 EXPECT_EQ(Search.getIncludeNameForHeader(FE), "Foo/Foo.h");
254}
255
256// Helper struct with null terminator character to make MemoryBuffer happy.
257template <class FileTy, class PaddingTy>
258struct NullTerminatedFile : public FileTy {
259 PaddingTy Padding = 0;
260};
261
262TEST_F(HeaderSearchTest, HeaderMapReverseLookup) {
263 typedef NullTerminatedFile<test::HMapFileMock<2, 32>, char> FileTy;
264 FileTy File;
265 File.init();
266
267 test::HMapFileMockMaker<FileTy> Maker(File);
268 auto a = Maker.addString(S: "d.h");
269 auto b = Maker.addString(S: "b/");
270 auto c = Maker.addString(S: "c.h");
271 Maker.addBucket(Str: "d.h", Key: a, Prefix: b, Suffix: c);
272
273 addHeaderMap(Filename: "/x/y/z.hmap", Buf: File.getBuffer());
274 addSearchDir(Dir: "/a");
275
276 EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c.h",
277 /*WorkingDir=*/"",
278 /*MainFile=*/""),
279 "d.h");
280}
281
282TEST_F(HeaderSearchTest, HeaderMapFrameworkLookup) {
283 typedef NullTerminatedFile<test::HMapFileMock<4, 128>, char> FileTy;
284 FileTy File;
285 File.init();
286
287 std::string HeaderDirName = "/tmp/Sources/Foo/Headers/";
288 std::string HeaderName = "Foo.h";
289 if (is_style_windows(S: llvm::sys::path::Style::native)) {
290 // Force header path to be absolute on windows.
291 // As headermap content should represent absolute locations.
292 HeaderDirName = "C:" + HeaderDirName;
293 }
294
295 test::HMapFileMockMaker<FileTy> Maker(File);
296 auto a = Maker.addString(S: "Foo/Foo.h");
297 auto b = Maker.addString(S: HeaderDirName);
298 auto c = Maker.addString(S: HeaderName);
299 Maker.addBucket(Str: "Foo/Foo.h", Key: a, Prefix: b, Suffix: c);
300 addHeaderMap(Filename: "product-headers.hmap", Buf: File.getBuffer(), /*isAngled=*/true);
301
302 VFS->addFile(
303 Path: HeaderDirName + HeaderName, ModificationTime: 0,
304 Buffer: llvm::MemoryBuffer::getMemBufferCopy(InputData: "", BufferName: HeaderDirName + HeaderName),
305 /*User=*/std::nullopt, /*Group=*/std::nullopt,
306 Type: llvm::sys::fs::file_type::regular_file);
307
308 bool IsMapped = false;
309 auto FoundFile = Search.LookupFile(
310 Filename: "Foo/Foo.h", IncludeLoc: SourceLocation(), /*isAngled=*/true, /*FromDir=*/nullptr,
311 /*CurDir=*/nullptr, /*Includers=*/{}, /*SearchPath=*/nullptr,
312 /*RelativePath=*/nullptr, /*RequestingModule=*/nullptr,
313 /*SuggestedModule=*/nullptr, IsMapped: &IsMapped,
314 /*IsFrameworkFound=*/nullptr);
315
316 EXPECT_TRUE(FoundFile.has_value());
317 EXPECT_TRUE(IsMapped);
318 auto &FE = *FoundFile;
319 auto FI = Search.getExistingFileInfo(FE);
320 EXPECT_TRUE(FI);
321 EXPECT_TRUE(FI->IsValid);
322 EXPECT_EQ(Search.getIncludeNameForHeader(FE), "Foo/Foo.h");
323}
324
325TEST_F(HeaderSearchTest, HeaderFileInfoMerge) {
326 auto AddHeader = [&](std::string HeaderPath) -> FileEntryRef {
327 VFS->addFile(Path: HeaderPath, ModificationTime: 0,
328 Buffer: llvm::MemoryBuffer::getMemBufferCopy(InputData: "", BufferName: HeaderPath),
329 /*User=*/std::nullopt, /*Group=*/std::nullopt,
330 Type: llvm::sys::fs::file_type::regular_file);
331 return *FileMgr.getOptionalFileRef(Filename: HeaderPath);
332 };
333
334 class MockExternalHeaderFileInfoSource : public ExternalHeaderFileInfoSource {
335 HeaderFileInfo GetHeaderFileInfo(FileEntryRef FE) {
336 HeaderFileInfo HFI;
337 auto FileName = FE.getName();
338 if (FileName == ModularPath)
339 HFI.mergeModuleMembership(Role: ModuleMap::NormalHeader);
340 else if (FileName == TextualPath)
341 HFI.mergeModuleMembership(Role: ModuleMap::TextualHeader);
342 HFI.External = true;
343 HFI.IsValid = true;
344 return HFI;
345 }
346
347 public:
348 std::string ModularPath = "/modular.h";
349 std::string TextualPath = "/textual.h";
350 };
351
352 auto ExternalSource = std::make_unique<MockExternalHeaderFileInfoSource>();
353 Search.SetExternalSource(ExternalSource.get());
354
355 // Everything should start out external.
356 auto ModularFE = AddHeader(ExternalSource->ModularPath);
357 auto TextualFE = AddHeader(ExternalSource->TextualPath);
358 EXPECT_TRUE(Search.getExistingFileInfo(ModularFE)->External);
359 EXPECT_TRUE(Search.getExistingFileInfo(TextualFE)->External);
360
361 // Marking the same role should keep it external
362 Search.MarkFileModuleHeader(FE: ModularFE, Role: ModuleMap::NormalHeader,
363 /*isCompilingModuleHeader=*/false);
364 Search.MarkFileModuleHeader(FE: TextualFE, Role: ModuleMap::TextualHeader,
365 /*isCompilingModuleHeader=*/false);
366 EXPECT_TRUE(Search.getExistingFileInfo(ModularFE)->External);
367 EXPECT_TRUE(Search.getExistingFileInfo(TextualFE)->External);
368
369 // textual -> modular should update the HFI, but modular -> textual should be
370 // a no-op.
371 Search.MarkFileModuleHeader(FE: ModularFE, Role: ModuleMap::TextualHeader,
372 /*isCompilingModuleHeader=*/false);
373 Search.MarkFileModuleHeader(FE: TextualFE, Role: ModuleMap::NormalHeader,
374 /*isCompilingModuleHeader=*/false);
375 auto ModularFI = Search.getExistingFileInfo(FE: ModularFE);
376 auto TextualFI = Search.getExistingFileInfo(FE: TextualFE);
377 EXPECT_TRUE(ModularFI->External);
378 EXPECT_TRUE(ModularFI->isModuleHeader);
379 EXPECT_FALSE(ModularFI->isTextualModuleHeader);
380 EXPECT_FALSE(TextualFI->External);
381 EXPECT_TRUE(TextualFI->isModuleHeader);
382 EXPECT_FALSE(TextualFI->isTextualModuleHeader);
383
384 // Compiling the module should make the HFI local.
385 Search.MarkFileModuleHeader(FE: ModularFE, Role: ModuleMap::NormalHeader,
386 /*isCompilingModuleHeader=*/true);
387 Search.MarkFileModuleHeader(FE: TextualFE, Role: ModuleMap::NormalHeader,
388 /*isCompilingModuleHeader=*/true);
389 EXPECT_FALSE(Search.getExistingFileInfo(ModularFE)->External);
390 EXPECT_FALSE(Search.getExistingFileInfo(TextualFE)->External);
391}
392
393} // namespace
394} // namespace clang
395

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang/unittests/Lex/HeaderSearchTest.cpp