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
37namespace clang {
38namespace {
39
40class SearchPathTest : public ::testing::Test {
41protected:
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 makeHeaderSearchFromCC1Args(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 HeaderInfo = 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
78TEST_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 HeaderInfo = 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

source code of clang/unittests/Frontend/SearchPathTest.cpp