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 "gtest/gtest.h"
22 
23 namespace clang {
24 namespace {
25 
26 // The test fixture.
27 class HeaderSearchTest : public ::testing::Test {
28 protected:
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 
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 
49   void addHeaderMap(llvm::StringRef Filename,
50                     std::unique_ptr<llvm::MemoryBuffer> Buf) {
51     VFS->addFile(Filename, 0, std::move(Buf), /*User=*/None, /*Group=*/None,
52                  llvm::sys::fs::file_type::regular_file);
53     auto FE = FileMgr.getFile(Filename, true);
54     assert(FE);
55 
56     // Test class supports only one HMap at a time.
57     assert(!HMap);
58     HMap = HeaderMap::Create(*FE, FileMgr);
59     auto DL =
60         DirectoryLookup(HMap.get(), SrcMgr::C_User, /*isFramework=*/false);
61     Search.AddSearchPath(DL, /*isAngled=*/false);
62   }
63 
64   IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS;
65   FileSystemOptions FileMgrOpts;
66   FileManager FileMgr;
67   IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
68   DiagnosticsEngine Diags;
69   SourceManager SourceMgr;
70   LangOptions LangOpts;
71   std::shared_ptr<TargetOptions> TargetOpts;
72   IntrusiveRefCntPtr<TargetInfo> Target;
73   HeaderSearch Search;
74   std::unique_ptr<HeaderMap> HMap;
75 };
76 
77 TEST_F(HeaderSearchTest, NoSearchDir) {
78   EXPECT_EQ(Search.search_dir_size(), 0u);
79   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"",
80                                                    /*MainFile=*/""),
81             "/x/y/z");
82 }
83 
84 TEST_F(HeaderSearchTest, SimpleShorten) {
85   addSearchDir("/x");
86   addSearchDir("/x/y");
87   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/z", /*WorkingDir=*/"",
88                                                    /*MainFile=*/""),
89             "z");
90   addSearchDir("/a/b/");
91   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c", /*WorkingDir=*/"",
92                                                    /*MainFile=*/""),
93             "c");
94 }
95 
96 TEST_F(HeaderSearchTest, ShortenWithWorkingDir) {
97   addSearchDir("x/y");
98   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c/x/y/z",
99                                                    /*WorkingDir=*/"/a/b/c",
100                                                    /*MainFile=*/""),
101             "z");
102 }
103 
104 TEST_F(HeaderSearchTest, Dots) {
105   addSearchDir("/x/./y/");
106   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/x/y/./z",
107                                                    /*WorkingDir=*/"",
108                                                    /*MainFile=*/""),
109             "z");
110   addSearchDir("a/.././c/");
111   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/m/n/./c/z",
112                                                    /*WorkingDir=*/"/m/n/",
113                                                    /*MainFile=*/""),
114             "z");
115 }
116 
117 #ifdef _WIN32
118 TEST_F(HeaderSearchTest, BackSlash) {
119   addSearchDir("C:\\x\\y\\");
120   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t",
121                                                    /*WorkingDir=*/"",
122                                                    /*MainFile=*/""),
123             "z/t");
124 }
125 
126 TEST_F(HeaderSearchTest, BackSlashWithDotDot) {
127   addSearchDir("..\\y");
128   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t",
129                                                    /*WorkingDir=*/"C:/x/y/",
130                                                    /*MainFile=*/""),
131             "z/t");
132 }
133 #endif
134 
135 TEST_F(HeaderSearchTest, DotDotsWithAbsPath) {
136   addSearchDir("/x/../y/");
137   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z",
138                                                    /*WorkingDir=*/"",
139                                                    /*MainFile=*/""),
140             "z");
141 }
142 
143 TEST_F(HeaderSearchTest, IncludeFromSameDirectory) {
144   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z/t.h",
145                                                    /*WorkingDir=*/"",
146                                                    /*MainFile=*/"/y/a.cc"),
147             "z/t.h");
148 
149   addSearchDir("/");
150   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z/t.h",
151                                                    /*WorkingDir=*/"",
152                                                    /*MainFile=*/"/y/a.cc"),
153             "y/z/t.h");
154 }
155 
156 // Helper struct with null terminator character to make MemoryBuffer happy.
157 template <class FileTy, class PaddingTy>
158 struct NullTerminatedFile : public FileTy {
159   PaddingTy Padding = 0;
160 };
161 
162 TEST_F(HeaderSearchTest, HeaderMapReverseLookup) {
163   typedef NullTerminatedFile<test::HMapFileMock<2, 32>, char> FileTy;
164   FileTy File;
165   File.init();
166 
167   test::HMapFileMockMaker<FileTy> Maker(File);
168   auto a = Maker.addString("d.h");
169   auto b = Maker.addString("b/");
170   auto c = Maker.addString("c.h");
171   Maker.addBucket("d.h", a, b, c);
172 
173   addHeaderMap("/x/y/z.hmap", File.getBuffer());
174   addSearchDir("/a");
175 
176   EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/a/b/c.h",
177                                                    /*WorkingDir=*/"",
178                                                    /*MainFile=*/""),
179             "d.h");
180 }
181 
182 } // namespace
183 } // namespace clang
184