diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 351e1ad4e1b03..6f9cccbbeca12 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -8451,6 +8451,11 @@ def objc_isystem : Separate<["-"], "objc-isystem">, def objcxx_isystem : Separate<["-"], "objcxx-isystem">, MetaVarName<"">, HelpText<"Add directory to the ObjC++ SYSTEM include search path">; +def internal_iframework : Separate<["-"], "internal-iframework">, + MetaVarName<"">, + HelpText<"Add directory to the internal system framework search path; these " + "are assumed to not be user-provided and are used to model system " + "and standard frameworks' paths.">; def internal_isystem : Separate<["-"], "internal-isystem">, MetaVarName<"">, HelpText<"Add directory to the internal system include search path; these " diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h index d0059673d6a67..58edf2b3887b0 100644 --- a/clang/include/clang/Driver/ToolChain.h +++ b/clang/include/clang/Driver/ToolChain.h @@ -226,6 +226,9 @@ class ToolChain { /// \name Utilities for implementing subclasses. ///@{ + static void addSystemFrameworkInclude(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args, + const Twine &Path); static void addSystemInclude(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, const Twine &Path); @@ -236,6 +239,9 @@ class ToolChain { addExternCSystemIncludeIfExists(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, const Twine &Path); + static void addSystemFrameworkIncludes(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args, + ArrayRef Paths); static void addSystemIncludes(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, ArrayRef Paths); diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index 4619b8c1887be..f676b12c99a24 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -67,14 +67,15 @@ static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum, return true; // Some include flags shouldn't be skipped if we have a crash VFS - IsInclude = llvm::StringSwitch(Flag) - .Cases("-include", "-header-include-file", true) - .Cases("-idirafter", "-internal-isystem", "-iwithprefix", true) - .Cases("-internal-externc-isystem", "-iprefix", true) - .Cases("-iwithprefixbefore", "-isystem", "-iquote", true) - .Cases("-isysroot", "-I", "-F", "-resource-dir", true) - .Cases("-iframework", "-include-pch", true) - .Default(false); + IsInclude = + llvm::StringSwitch(Flag) + .Cases("-include", "-header-include-file", true) + .Cases("-idirafter", "-internal-isystem", "-iwithprefix", true) + .Cases("-internal-externc-isystem", "-iprefix", true) + .Cases("-iwithprefixbefore", "-isystem", "-iquote", true) + .Cases("-isysroot", "-I", "-F", "-resource-dir", true) + .Cases("-internal-iframework", "-iframework", "-include-pch", true) + .Default(false); if (IsInclude) return !HaveCrashVFS; diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 5cd5755e01587..3c52abb0ab78e 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -1366,10 +1366,17 @@ ToolChain::CXXStdlibType ToolChain::GetCXXStdlibType(const ArgList &Args) const{ return *cxxStdlibType; } +/// Utility function to add a system framework directory to CC1 arguments. +void ToolChain::addSystemFrameworkInclude(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args, + const Twine &Path) { + CC1Args.push_back("-internal-iframework"); + CC1Args.push_back(DriverArgs.MakeArgString(Path)); +} + /// Utility function to add a system include directory to CC1 arguments. -/*static*/ void ToolChain::addSystemInclude(const ArgList &DriverArgs, - ArgStringList &CC1Args, - const Twine &Path) { +void ToolChain::addSystemInclude(const ArgList &DriverArgs, + ArgStringList &CC1Args, const Twine &Path) { CC1Args.push_back("-internal-isystem"); CC1Args.push_back(DriverArgs.MakeArgString(Path)); } @@ -1382,9 +1389,9 @@ ToolChain::CXXStdlibType ToolChain::GetCXXStdlibType(const ArgList &Args) const{ /// "C" semantics. These semantics are *ignored* by and large today, but its /// important to preserve the preprocessor changes resulting from the /// classification. -/*static*/ void ToolChain::addExternCSystemInclude(const ArgList &DriverArgs, - ArgStringList &CC1Args, - const Twine &Path) { +void ToolChain::addExternCSystemInclude(const ArgList &DriverArgs, + ArgStringList &CC1Args, + const Twine &Path) { CC1Args.push_back("-internal-externc-isystem"); CC1Args.push_back(DriverArgs.MakeArgString(Path)); } @@ -1396,19 +1403,28 @@ void ToolChain::addExternCSystemIncludeIfExists(const ArgList &DriverArgs, addExternCSystemInclude(DriverArgs, CC1Args, Path); } +/// Utility function to add a list of system framework directories to CC1. +void ToolChain::addSystemFrameworkIncludes(const ArgList &DriverArgs, + ArgStringList &CC1Args, + ArrayRef Paths) { + for (const auto &Path : Paths) { + CC1Args.push_back("-internal-iframework"); + CC1Args.push_back(DriverArgs.MakeArgString(Path)); + } +} + /// Utility function to add a list of system include directories to CC1. -/*static*/ void ToolChain::addSystemIncludes(const ArgList &DriverArgs, - ArgStringList &CC1Args, - ArrayRef Paths) { +void ToolChain::addSystemIncludes(const ArgList &DriverArgs, + ArgStringList &CC1Args, + ArrayRef Paths) { for (const auto &Path : Paths) { CC1Args.push_back("-internal-isystem"); CC1Args.push_back(DriverArgs.MakeArgString(Path)); } } -/*static*/ std::string ToolChain::concat(StringRef Path, const Twine &A, - const Twine &B, const Twine &C, - const Twine &D) { +std::string ToolChain::concat(StringRef Path, const Twine &A, const Twine &B, + const Twine &C, const Twine &D) { SmallString<128> Result(Path); llvm::sys::path::append(Result, llvm::sys::path::Style::posix, A, B, C, D); return std::string(Result); diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 4735dc3ad30ee..76fa2d121db2a 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -2577,6 +2577,27 @@ void AppleMachO::AddClangSystemIncludeArgs( } } +void DarwinClang::AddClangSystemIncludeArgs( + const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const { + AppleMachO::AddClangSystemIncludeArgs(DriverArgs, CC1Args); + + if (DriverArgs.hasArg(options::OPT_nostdinc) || + DriverArgs.hasArg(options::OPT_nostdlibinc)) + return; + + llvm::SmallString<128> Sysroot = GetEffectiveSysroot(DriverArgs); + + // Add /System/Library/Frameworks + // Add /System/Library/SubFrameworks + // Add /Library/Frameworks + SmallString<128> P1(Sysroot), P2(Sysroot), P3(Sysroot); + llvm::sys::path::append(P1, "System", "Library", "Frameworks"); + llvm::sys::path::append(P2, "System", "Library", "SubFrameworks"); + llvm::sys::path::append(P3, "Library", "Frameworks"); + addSystemFrameworkIncludes(DriverArgs, CC1Args, {P1, P2, P3}); +} + bool DarwinClang::AddGnuCPlusPlusIncludePaths(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, llvm::SmallString<128> Base, diff --git a/clang/lib/Driver/ToolChains/Darwin.h b/clang/lib/Driver/ToolChains/Darwin.h index 76523d636ce07..b38bfe6d1e554 100644 --- a/clang/lib/Driver/ToolChains/Darwin.h +++ b/clang/lib/Driver/ToolChains/Darwin.h @@ -647,6 +647,10 @@ class LLVM_LIBRARY_VISIBILITY DarwinClang : public Darwin { /// @name Apple ToolChain Implementation /// { + void + AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + RuntimeLibType GetRuntimeLibType(const llvm::opt::ArgList &Args) const override; void AddLinkRuntimeLibArgs(const llvm::opt::ArgList &Args, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index a0b8bbf9d827f..394512978b521 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3373,6 +3373,8 @@ static void GenerateHeaderSearchArgs(const HeaderSearchOptions &Opts, : OPT_internal_externc_isystem; GenerateArg(Consumer, Opt, It->Path); } + for (; It < End && Matches(*It, {frontend::System}, true, true); ++It) + GenerateArg(Consumer, OPT_internal_iframework, It->Path); assert(It == End && "Unhandled HeaderSearchOption::Entry."); @@ -3505,6 +3507,8 @@ static bool ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args, Group = frontend::ExternCSystem; Opts.AddPath(A->getValue(), Group, false, true); } + for (const auto *A : Args.filtered(OPT_internal_iframework)) + Opts.AddPath(A->getValue(), frontend::System, true, true); // Add the path prefixes which are implicitly treated as being system headers. for (const auto *A : diff --git a/clang/lib/Lex/InitHeaderSearch.cpp b/clang/lib/Lex/InitHeaderSearch.cpp index bb2a21356fa8f..df45c96725fe3 100644 --- a/clang/lib/Lex/InitHeaderSearch.cpp +++ b/clang/lib/Lex/InitHeaderSearch.cpp @@ -321,6 +321,9 @@ bool InitHeaderSearch::ShouldAddDefaultIncludePaths( break; } + if (triple.isOSDarwin()) + return false; + return true; // Everything else uses AddDefaultIncludePaths(). } @@ -335,22 +338,6 @@ void InitHeaderSearch::AddDefaultIncludePaths( if (!ShouldAddDefaultIncludePaths(triple)) return; - // NOTE: some additional header search logic is handled in the driver for - // Darwin. - if (triple.isOSDarwin()) { - if (HSOpts.UseStandardSystemIncludes) { - // Add the default framework include paths on Darwin. - if (triple.isDriverKit()) { - AddPath("/System/DriverKit/System/Library/Frameworks", System, true); - } else { - AddPath("/System/Library/Frameworks", System, true); - AddPath("/System/Library/SubFrameworks", System, true); - AddPath("/Library/Frameworks", System, true); - } - } - return; - } - if (Lang.CPlusPlus && !Lang.AsmPreprocessor && HSOpts.UseStandardCXXIncludes && HSOpts.UseStandardSystemIncludes) { if (HSOpts.UseLibcxx) { diff --git a/clang/test/Driver/Inputs/DriverKit19.0.sdk/System/DriverKit/System/Library/SubFrameworks/.keep b/clang/test/Driver/Inputs/DriverKit19.0.sdk/System/DriverKit/System/Library/SubFrameworks/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/MacOSX15.1.sdk/Library/Frameworks/.keep b/clang/test/Driver/Inputs/MacOSX15.1.sdk/Library/Frameworks/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/MacOSX15.1.sdk/System/Library/Frameworks/.keep b/clang/test/Driver/Inputs/MacOSX15.1.sdk/System/Library/Frameworks/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/MacOSX15.1.sdk/System/Library/SubFrameworks/.keep b/clang/test/Driver/Inputs/MacOSX15.1.sdk/System/Library/SubFrameworks/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/darwin-framework-search-paths.c b/clang/test/Driver/darwin-framework-search-paths.c new file mode 100644 index 0000000000000..1cb4dc420c536 --- /dev/null +++ b/clang/test/Driver/darwin-framework-search-paths.c @@ -0,0 +1,23 @@ +// UNSUPPORTED: system-windows +// Windows is unsupported because we use the Unix path separator `/` in the test. + +// RUN: %clang %s -target arm64-apple-macosx15.1 -isysroot %S/Inputs/MacOSX15.1.sdk -c %s -### 2>&1 \ +// RUN: | FileCheck -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s +// +// CHECK: "-cc1" +// CHECK: "-resource-dir" "[[RESOURCE_DIR:[^"]*]]" +// CHECK-SAME: "-internal-iframework" "[[SDKROOT]]/System/Library/Frameworks" +// CHECK-SAME: "-internal-iframework" "[[SDKROOT]]/System/Library/SubFrameworks" +// CHECK-SAME: "-internal-iframework" "[[SDKROOT]]/Library/Frameworks" + +// Verify that -nostdlibinc and -nostdinc removes the default search paths. +// +// RUN: %clang %s -target arm64-apple-macosx15.1 -isysroot %S/Inputs/MacOSX15.1.sdk -nostdinc -c %s -### 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOSTD -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s +// +// RUN: %clang %s -target arm64-apple-macosx15.1 -isysroot %S/Inputs/MacOSX15.1.sdk -nostdlibinc -c %s -### 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOSTD -DSDKROOT=%S/Inputs/MacOSX15.1.sdk %s +// +// CHECK-NOSTD: "-cc1" +// CHECK-NOSTD: "-resource-dir" "[[RESOURCE_DIR:[^"]*]]" +// CHECK-NOSTD-NOT: "-internal-iframework" diff --git a/clang/test/Driver/darwin-subframeworks.c b/clang/test/Driver/darwin-subframeworks.c deleted file mode 100644 index 1a7a095c64292..0000000000000 --- a/clang/test/Driver/darwin-subframeworks.c +++ /dev/null @@ -1,18 +0,0 @@ -// UNSUPPORTED: system-windows -// Windows is unsupported because we use the Unix path separator `\`. - -// Add default directories before running clang to check default -// search paths. -// RUN: rm -rf %t && mkdir -p %t -// RUN: cp -R %S/Inputs/MacOSX15.1.sdk %t/ -// RUN: mkdir -p %t/MacOSX15.1.sdk/System/Library/Frameworks -// RUN: mkdir -p %t/MacOSX15.1.sdk/System/Library/SubFrameworks -// RUN: mkdir -p %t/MacOSX15.1.sdk/usr/include - -// RUN: %clang %s -target arm64-apple-darwin13.0 -isysroot %t/MacOSX15.1.sdk -E -v 2>&1 | FileCheck %s - -// CHECK: -isysroot [[PATH:[^ ]*/MacOSX15.1.sdk]] -// CHECK: #include <...> search starts here: -// CHECK: [[PATH]]/usr/include -// CHECK: [[PATH]]/System/Library/Frameworks (framework directory) -// CHECK: [[PATH]]/System/Library/SubFrameworks (framework directory) diff --git a/clang/test/Driver/driverkit-path.c b/clang/test/Driver/driverkit-path.c index 3caae382d65bb..32f0a6721ab77 100644 --- a/clang/test/Driver/driverkit-path.c +++ b/clang/test/Driver/driverkit-path.c @@ -21,13 +21,15 @@ int main() { return 0; } // LD64-NEW: "-isysroot" "[[PATH:[^"]*]]Inputs/DriverKit19.0.sdk" // LD64-NEW-NOT: "-L[[PATH]]Inputs/DriverKit19.0.sdk/System/DriverKit/usr/lib" // LD64-NEW-NOT: "-F[[PATH]]Inputs/DriverKit19.0.sdk/System/DriverKit/System/Library/Frameworks" +// LD64-NEW-NOT: "-F[[PATH]]Inputs/DriverKit19.0.sdk/System/DriverKit/System/Library/SubFrameworks" -// RUN: %clang %s -target x86_64-apple-driverkit19.0 -isysroot %S/Inputs/DriverKit19.0.sdk -E -v -x c++ 2>&1 | FileCheck %s --check-prefix=INC +// RUN: %clang %s -target x86_64-apple-driverkit19.0 -isysroot %S/Inputs/DriverKit19.0.sdk -x c++ -### 2>&1 \ +// RUN: | FileCheck %s -DSDKROOT=%S/Inputs/DriverKit19.0.sdk --check-prefix=INC // -// INC: -isysroot [[PATH:[^ ]*/Inputs/DriverKit19.0.sdk]] -// INC-LABEL: #include <...> search starts here: -// INC: [[PATH]]/System/DriverKit/usr/local/include -// INC: /lib{{(64)?}}/clang/{{[^/ ]+}}/include -// INC: [[PATH]]/System/DriverKit/usr/include -// INC: [[PATH]]/System/DriverKit/System/Library/Frameworks (framework directory) +// INC: "-isysroot" "[[SDKROOT]]" +// INC: "-internal-isystem" "[[SDKROOT]]/System/DriverKit/usr/local/include" +// INC: "-internal-isystem" "{{.+}}/lib{{(64)?}}/clang/{{[^/ ]+}}/include" +// INC: "-internal-externc-isystem" "[[SDKROOT]]/System/DriverKit/usr/include" +// INC: "-internal-iframework" "[[SDKROOT]]/System/DriverKit/System/Library/Frameworks" +// INC: "-internal-iframework" "[[SDKROOT]]/System/DriverKit/System/Library/SubFrameworks" diff --git a/clang/test/Preprocessor/cuda-macos-includes.cu b/clang/test/Preprocessor/cuda-macos-includes.cu deleted file mode 100644 index 6ef94b0e45352..0000000000000 --- a/clang/test/Preprocessor/cuda-macos-includes.cu +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %clang -cc1 -fcuda-is-device -isysroot /var/empty \ -// RUN: -triple nvptx-nvidia-cuda -aux-triple i386-apple-macosx \ -// RUN: -E -fcuda-is-device -v -o /dev/null -x cuda %s 2>&1 | FileCheck %s - -// RUN: %clang -cc1 -isysroot /var/empty \ -// RUN: -triple i386-apple-macosx -aux-triple nvptx-nvidia-cuda \ -// RUN: -E -fcuda-is-device -v -o /dev/null -x cuda %s 2>&1 | FileCheck %s - -// Check that when we do CUDA host and device compiles on MacOS, we check for -// includes in /System/Library/Frameworks and /Library/Frameworks. - -// CHECK-DAG: ignoring nonexistent directory "/var/empty/System/Library/Frameworks" -// CHECK-DAG: ignoring nonexistent directory "/var/empty/Library/Frameworks" diff --git a/clang/unittests/Frontend/CMakeLists.txt b/clang/unittests/Frontend/CMakeLists.txt index bbf0396014fa9..0d9fc714212d5 100644 --- a/clang/unittests/Frontend/CMakeLists.txt +++ b/clang/unittests/Frontend/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_unittest(FrontendTests PCHPreambleTest.cpp ReparseWorkingDirTest.cpp OutputStreamTest.cpp + SearchPathTest.cpp TextDiagnosticTest.cpp UtilsTest.cpp CLANG_LIBS diff --git a/clang/unittests/Frontend/SearchPathTest.cpp b/clang/unittests/Frontend/SearchPathTest.cpp new file mode 100644 index 0000000000000..5d382a4ee20a8 --- /dev/null +++ b/clang/unittests/Frontend/SearchPathTest.cpp @@ -0,0 +1,157 @@ +//====-- unittests/Frontend/SearchPathTest.cpp - FrontendAction tests -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemOptions.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" + +#include "gtest/gtest.h" + +#include +#include +#include +#include + +namespace clang { +namespace { + +class SearchPathTest : public ::testing::Test { +protected: + SearchPathTest() + : Diags(new DiagnosticIDs(), new DiagnosticOptions, + new IgnoringDiagConsumer()), + VFS(new llvm::vfs::InMemoryFileSystem), + FileMgr(FileSystemOptions(), VFS), SourceMgr(Diags, FileMgr), + Invocation(std::make_unique()) {} + + DiagnosticsEngine Diags; + IntrusiveRefCntPtr VFS; + FileManager FileMgr; + SourceManager SourceMgr; + std::unique_ptr Invocation; + + void addDirectories(ArrayRef Dirs) { + for (StringRef Dir : Dirs) { + VFS->addFile(Dir, 0, llvm::MemoryBuffer::getMemBuffer(""), + /*User=*/std::nullopt, /*Group=*/std::nullopt, + llvm::sys::fs::file_type::directory_file); + } + } + + std::unique_ptr + makeHeaderSearchFromCC1Args(llvm::opt::ArgStringList Args) { + CompilerInvocation::CreateFromArgs(*Invocation, Args, Diags); + HeaderSearchOptions HSOpts = Invocation->getHeaderSearchOpts(); + LangOptions LangOpts = Invocation->getLangOpts(); + TargetInfo *Target = + TargetInfo::CreateTargetInfo(Diags, Invocation->getTargetOpts()); + auto HeaderInfo = std::make_unique(HSOpts, SourceMgr, Diags, + LangOpts, Target); + ApplyHeaderSearchOptions(*HeaderInfo, HSOpts, LangOpts, + Target->getTriple()); + return HeaderInfo; + } +}; + +TEST_F(SearchPathTest, SearchPathOrder) { + addDirectories({"One", "Two", "Three", "Four", "Five", "Six", "Seven", + "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", + "Fourteen", "Fifteen", "Sixteen", "Seventeen"}); + llvm::opt::ArgStringList Args = { + // Make sure to use a triple and language that don't automatically add any + // search paths. + "-triple", "arm64-apple-darwin24.4.0", "-x", "c", + + // clang-format off + "-internal-isystem", "One", + "-iwithsysroot", "Two", + "-c-isystem", "Three", + "-IFour", + "-idirafter", "Five", + "-internal-externc-isystem", "Six", + "-iwithprefix", "Seven", + "-FEight", + "-idirafter", "Nine", + "-iframeworkwithsysroot", "Ten", + "-internal-iframework", "Eleven", + "-iframework", "Twelve", + "-iwithprefixbefore", "Thirteen", + "-internal-isystem", "Fourteen", + "-isystem", "Fifteen", + "-ISixteen", + "-iwithsysroot", "Seventeen", + // clang-format on + }; + + // The search path arguments get categorized by IncludeDirGroup, but + // ultimately are sorted with some groups mixed together and some flags sorted + // very specifically within their group. The conceptual groups below don't + // exactly correspond to IncludeDirGroup. + const std::vector expected = { + // User paths: -I and -F mixed together, -iwithprefixbefore. + /*-I*/ "Four", + /*-F*/ "Eight", + /*-I*/ "Sixteen", + /*-iwithprefixbefore*/ "Thirteen", + + // System paths: -isystem and -iwithsysroot, -iframework, + // -iframeworkwithsysroot, one of {-c-isystem, -cxx-isystem, + // -objc-isystem, -objcxx-isystem} + /*-iwithsysroot*/ "Two", + /*-isystem*/ "Fifteen", + /*-iwithsysroot*/ "Seventeen", + /*-iframework*/ "Twelve", + /*-iframeworkwithsysroot*/ "Ten", + /*-c-isystem*/ "Three", + + // Internal paths: -internal-isystem and -internal-externc-isystem, + // -internal-iframework + /*-internal-isystem*/ "One", + /*-internal-externc-isystem*/ "Six", + /*-internal-isystem*/ "Fourteen", + /*-internal-iframework*/ "Eleven", + + // After paths: -iwithprefix, -idirafter + /*-iwithprefix*/ "Seven", + /*-idirafter*/ "Five", + /*-idirafter*/ "Nine", + }; + + auto HeaderInfo = makeHeaderSearchFromCC1Args(Args); + ConstSearchDirRange SearchDirs(HeaderInfo->angled_dir_begin(), + HeaderInfo->search_dir_end()); + for (auto SearchPaths : zip_longest(SearchDirs, expected)) { + auto ActualDirectory = std::get<0>(SearchPaths); + EXPECT_TRUE(ActualDirectory.has_value()); + auto ExpectedPath = std::get<1>(SearchPaths); + EXPECT_TRUE(ExpectedPath.has_value()); + if (ActualDirectory && ExpectedPath) { + EXPECT_EQ(ActualDirectory->getName(), *ExpectedPath); + } + } +} + +} // namespace +} // namespace clang