| 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 | |