aboutsummaryrefslogtreecommitdiff
path: root/clang/unittests/Frontend/SearchPathTest.cpp
blob: c74a5c75fada55fe14e2dddd1be7c6a59eaccdfe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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 <memory>
#include <optional>
#include <tuple>
#include <vector>

namespace clang {
namespace {

class SearchPathTest : public ::testing::Test {
protected:
  SearchPathTest()
      : Diags(new DiagnosticIDs(), DiagOpts, new IgnoringDiagConsumer()),
        VFS(new 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