1 | //====-- unittests/Frontend/SearchPathTest.cpp - FrontendAction 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/Basic/Diagnostic.h" |
10 | #include "clang/Basic/DiagnosticIDs.h" |
11 | #include "clang/Basic/DiagnosticOptions.h" |
12 | #include "clang/Basic/FileManager.h" |
13 | #include "clang/Basic/FileSystemOptions.h" |
14 | #include "clang/Basic/LangOptions.h" |
15 | #include "clang/Basic/SourceManager.h" |
16 | #include "clang/Basic/TargetInfo.h" |
17 | #include "clang/Frontend/CompilerInvocation.h" |
18 | #include "clang/Lex/HeaderSearch.h" |
19 | #include "clang/Lex/HeaderSearchOptions.h" |
20 | |
21 | #include "llvm/ADT/ArrayRef.h" |
22 | #include "llvm/ADT/IntrusiveRefCntPtr.h" |
23 | #include "llvm/ADT/STLExtras.h" |
24 | #include "llvm/ADT/StringRef.h" |
25 | #include "llvm/Option/Option.h" |
26 | #include "llvm/Support/FileSystem.h" |
27 | #include "llvm/Support/MemoryBuffer.h" |
28 | #include "llvm/Support/VirtualFileSystem.h" |
29 | |
30 | #include "gtest/gtest.h" |
31 | |
32 | #include <memory> |
33 | #include <optional> |
34 | #include <tuple> |
35 | #include <vector> |
36 | |
37 | namespace clang { |
38 | namespace { |
39 | |
40 | class SearchPathTest : public ::testing::Test { |
41 | protected: |
42 | SearchPathTest() |
43 | : Diags(new DiagnosticIDs(), DiagOpts, new IgnoringDiagConsumer()), |
44 | VFS(new llvm::vfs::InMemoryFileSystem), |
45 | FileMgr(FileSystemOptions(), VFS), SourceMgr(Diags, FileMgr), |
46 | Invocation(std::make_unique<CompilerInvocation>()) {} |
47 | |
48 | DiagnosticOptions DiagOpts; |
49 | DiagnosticsEngine Diags; |
50 | IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS; |
51 | FileManager FileMgr; |
52 | SourceManager SourceMgr; |
53 | std::unique_ptr<CompilerInvocation> Invocation; |
54 | IntrusiveRefCntPtr<TargetInfo> Target; |
55 | |
56 | void addDirectories(ArrayRef<StringRef> Dirs) { |
57 | for (StringRef Dir : Dirs) { |
58 | VFS->addFile(Path: Dir, ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: "" ), |
59 | /*User=*/std::nullopt, /*Group=*/std::nullopt, |
60 | Type: llvm::sys::fs::file_type::directory_file); |
61 | } |
62 | } |
63 | |
64 | std::unique_ptr<HeaderSearch> |
65 | (llvm::opt::ArgStringList Args) { |
66 | CompilerInvocation::CreateFromArgs(Res&: *Invocation, CommandLineArgs: Args, Diags); |
67 | HeaderSearchOptions HSOpts = Invocation->getHeaderSearchOpts(); |
68 | LangOptions LangOpts = Invocation->getLangOpts(); |
69 | Target = TargetInfo::CreateTargetInfo(Diags, Opts&: Invocation->getTargetOpts()); |
70 | auto = std::make_unique<HeaderSearch>(args&: HSOpts, args&: SourceMgr, args&: Diags, |
71 | args&: LangOpts, args: Target.get()); |
72 | ApplyHeaderSearchOptions(HS&: *HeaderInfo, HSOpts, Lang: LangOpts, |
73 | triple: Target->getTriple()); |
74 | return HeaderInfo; |
75 | } |
76 | }; |
77 | |
78 | TEST_F(SearchPathTest, SearchPathOrder) { |
79 | addDirectories(Dirs: {"One" , "Two" , "Three" , "Four" , "Five" , "Six" , "Seven" , |
80 | "Eight" , "Nine" , "Ten" , "Eleven" , "Twelve" , "Thirteen" , |
81 | "Fourteen" , "Fifteen" , "Sixteen" , "Seventeen" }); |
82 | llvm::opt::ArgStringList Args = { |
83 | // Make sure to use a triple and language that don't automatically add any |
84 | // search paths. |
85 | "-triple" , "arm64-apple-darwin24.4.0" , "-x" , "c" , |
86 | |
87 | // clang-format off |
88 | "-internal-isystem" , "One" , |
89 | "-iwithsysroot" , "Two" , |
90 | "-c-isystem" , "Three" , |
91 | "-IFour" , |
92 | "-idirafter" , "Five" , |
93 | "-internal-externc-isystem" , "Six" , |
94 | "-iwithprefix" , "Seven" , |
95 | "-FEight" , |
96 | "-idirafter" , "Nine" , |
97 | "-iframeworkwithsysroot" , "Ten" , |
98 | "-internal-iframework" , "Eleven" , |
99 | "-iframework" , "Twelve" , |
100 | "-iwithprefixbefore" , "Thirteen" , |
101 | "-internal-isystem" , "Fourteen" , |
102 | "-isystem" , "Fifteen" , |
103 | "-ISixteen" , |
104 | "-iwithsysroot" , "Seventeen" , |
105 | // clang-format on |
106 | }; |
107 | |
108 | // The search path arguments get categorized by IncludeDirGroup, but |
109 | // ultimately are sorted with some groups mixed together and some flags sorted |
110 | // very specifically within their group. The conceptual groups below don't |
111 | // exactly correspond to IncludeDirGroup. |
112 | const std::vector<StringRef> expected = { |
113 | // User paths: -I and -F mixed together, -iwithprefixbefore. |
114 | /*-I*/ "Four" , |
115 | /*-F*/ "Eight" , |
116 | /*-I*/ "Sixteen" , |
117 | /*-iwithprefixbefore*/ "Thirteen" , |
118 | |
119 | // System paths: -isystem and -iwithsysroot, -iframework, |
120 | // -iframeworkwithsysroot, one of {-c-isystem, -cxx-isystem, |
121 | // -objc-isystem, -objcxx-isystem} |
122 | /*-iwithsysroot*/ "Two" , |
123 | /*-isystem*/ "Fifteen" , |
124 | /*-iwithsysroot*/ "Seventeen" , |
125 | /*-iframework*/ "Twelve" , |
126 | /*-iframeworkwithsysroot*/ "Ten" , |
127 | /*-c-isystem*/ "Three" , |
128 | |
129 | // Internal paths: -internal-isystem and -internal-externc-isystem, |
130 | // -internal-iframework |
131 | /*-internal-isystem*/ "One" , |
132 | /*-internal-externc-isystem*/ "Six" , |
133 | /*-internal-isystem*/ "Fourteen" , |
134 | /*-internal-iframework*/ "Eleven" , |
135 | |
136 | // After paths: -iwithprefix, -idirafter |
137 | /*-iwithprefix*/ "Seven" , |
138 | /*-idirafter*/ "Five" , |
139 | /*-idirafter*/ "Nine" , |
140 | }; |
141 | |
142 | auto = makeHeaderSearchFromCC1Args(Args); |
143 | ConstSearchDirRange SearchDirs(HeaderInfo->angled_dir_begin(), |
144 | HeaderInfo->search_dir_end()); |
145 | for (auto SearchPaths : zip_longest(t&: SearchDirs, u: expected)) { |
146 | auto ActualDirectory = std::get<0>(t&: SearchPaths); |
147 | EXPECT_TRUE(ActualDirectory.has_value()); |
148 | auto ExpectedPath = std::get<1>(t&: SearchPaths); |
149 | EXPECT_TRUE(ExpectedPath.has_value()); |
150 | if (ActualDirectory && ExpectedPath) { |
151 | EXPECT_EQ(ActualDirectory->getName(), *ExpectedPath); |
152 | } |
153 | } |
154 | } |
155 | |
156 | } // namespace |
157 | } // namespace clang |
158 | |