| //====-- 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 <memory> |
| #include <optional> |
| #include <tuple> |
| #include <vector> |
| |
| namespace clang { |
| namespace { |
| |
| class SearchPathTest : public ::testing::Test { |
| protected: |
| SearchPathTest() |
| : Diags(DiagnosticIDs::create(), DiagOpts, new IgnoringDiagConsumer()), |
| VFS(llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>()), |
| FileMgr(FileSystemOptions(), VFS), SourceMgr(Diags, FileMgr), |
| Invocation(std::make_unique<CompilerInvocation>()) {} |
| |
| DiagnosticOptions DiagOpts; |
| DiagnosticsEngine Diags; |
| IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS; |
| FileManager FileMgr; |
| SourceManager SourceMgr; |
| std::unique_ptr<CompilerInvocation> Invocation; |
| IntrusiveRefCntPtr<TargetInfo> Target; |
| |
| void addDirectories(ArrayRef<StringRef> 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<HeaderSearch> |
| makeHeaderSearchFromCC1Args(llvm::opt::ArgStringList Args) { |
| CompilerInvocation::CreateFromArgs(*Invocation, Args, Diags); |
| HeaderSearchOptions HSOpts = Invocation->getHeaderSearchOpts(); |
| LangOptions LangOpts = Invocation->getLangOpts(); |
| Target = TargetInfo::CreateTargetInfo(Diags, Invocation->getTargetOpts()); |
| auto HeaderInfo = std::make_unique<HeaderSearch>(HSOpts, SourceMgr, Diags, |
| LangOpts, Target.get()); |
| 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<StringRef> 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 |