clang-tools  10.0.0
IndexActionTests.cpp
Go to the documentation of this file.
1 //===------ IndexActionTests.cpp -------------------------------*- C++ -*-===//
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 "Headers.h"
10 #include "TestFS.h"
11 #include "index/IndexAction.h"
12 #include "clang/Tooling/Tooling.h"
13 #include "gmock/gmock.h"
14 #include "gtest/gtest.h"
15 
16 namespace clang {
17 namespace clangd {
18 namespace {
19 
20 using ::testing::AllOf;
21 using ::testing::ElementsAre;
22 using ::testing::Not;
23 using ::testing::Pair;
24 using ::testing::UnorderedElementsAre;
25 using ::testing::UnorderedPointwise;
26 
27 std::string toUri(llvm::StringRef Path) { return URI::create(Path).toString(); }
28 
29 MATCHER(IsTU, "") { return arg.Flags & IncludeGraphNode::SourceFlag::IsTU; }
30 
31 MATCHER_P(HasDigest, Digest, "") { return arg.Digest == Digest; }
32 
33 MATCHER_P(HasName, Name, "") { return arg.Name == Name; }
34 
35 MATCHER(HasSameURI, "") {
36  llvm::StringRef URI = ::testing::get<0>(arg);
37  const std::string &Path = ::testing::get<1>(arg);
38  return toUri(Path) == URI;
39 }
40 
41 ::testing::Matcher<const IncludeGraphNode &>
42 IncludesAre(const std::vector<std::string> &Includes) {
43  return ::testing::Field(&IncludeGraphNode::DirectIncludes,
44  UnorderedPointwise(HasSameURI(), Includes));
45 }
46 
47 void checkNodesAreInitialized(const IndexFileIn &IndexFile,
48  const std::vector<std::string> &Paths) {
49  ASSERT_TRUE(IndexFile.Sources);
50  EXPECT_THAT(Paths.size(), IndexFile.Sources->size());
51  for (llvm::StringRef Path : Paths) {
52  auto URI = toUri(Path);
53  const auto &Node = IndexFile.Sources->lookup(URI);
54  // Uninitialized nodes will have an empty URI.
55  EXPECT_EQ(Node.URI.data(), IndexFile.Sources->find(URI)->getKeyData());
56  }
57 }
58 
59 std::map<std::string, const IncludeGraphNode &> toMap(const IncludeGraph &IG) {
60  std::map<std::string, const IncludeGraphNode &> Nodes;
61  for (auto &I : IG)
62  Nodes.emplace(I.getKey(), I.getValue());
63  return Nodes;
64 }
65 
66 class IndexActionTest : public ::testing::Test {
67 public:
68  IndexActionTest() : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem) {}
69 
70  IndexFileIn
71  runIndexingAction(llvm::StringRef MainFilePath,
72  const std::vector<std::string> &ExtraArgs = {}) {
73  IndexFileIn IndexFile;
74  llvm::IntrusiveRefCntPtr<FileManager> Files(
75  new FileManager(FileSystemOptions(), InMemoryFileSystem));
76 
78  SymbolCollector::Options(),
79  [&](SymbolSlab S) { IndexFile.Symbols = std::move(S); },
80  [&](RefSlab R) { IndexFile.Refs = std::move(R); },
81  [&](RelationSlab R) { IndexFile.Relations = std::move(R); },
82  [&](IncludeGraph IG) { IndexFile.Sources = std::move(IG); });
83 
84  std::vector<std::string> Args = {"index_action", "-fsyntax-only",
85  "-xc++", "-std=c++11",
86  "-iquote", testRoot()};
87  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
88  Args.push_back(MainFilePath);
89 
90  tooling::ToolInvocation Invocation(
91  Args, std::move(Action), Files.get(),
92  std::make_shared<PCHContainerOperations>());
93 
94  Invocation.run();
95 
96  checkNodesAreInitialized(IndexFile, FilePaths);
97  return IndexFile;
98  }
99 
100  void addFile(llvm::StringRef Path, llvm::StringRef Content) {
101  InMemoryFileSystem->addFile(Path, 0,
102  llvm::MemoryBuffer::getMemBuffer(Content));
103  FilePaths.push_back(Path);
104  }
105 
106 protected:
107  std::vector<std::string> FilePaths;
108  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
109 };
110 
111 TEST_F(IndexActionTest, CollectIncludeGraph) {
112  std::string MainFilePath = testPath("main.cpp");
113  std::string MainCode = "#include \"level1.h\"";
114  std::string Level1HeaderPath = testPath("level1.h");
115  std::string Level1HeaderCode = "#include \"level2.h\"";
116  std::string Level2HeaderPath = testPath("level2.h");
117  std::string Level2HeaderCode = "";
118 
119  addFile(MainFilePath, MainCode);
120  addFile(Level1HeaderPath, Level1HeaderCode);
121  addFile(Level2HeaderPath, Level2HeaderCode);
122 
123  IndexFileIn IndexFile = runIndexingAction(MainFilePath);
124  auto Nodes = toMap(*IndexFile.Sources);
125 
126  EXPECT_THAT(Nodes,
127  UnorderedElementsAre(
128  Pair(toUri(MainFilePath),
129  AllOf(IsTU(), IncludesAre({Level1HeaderPath}),
130  HasDigest(digest(MainCode)))),
131  Pair(toUri(Level1HeaderPath),
132  AllOf(Not(IsTU()), IncludesAre({Level2HeaderPath}),
133  HasDigest(digest(Level1HeaderCode)))),
134  Pair(toUri(Level2HeaderPath),
135  AllOf(Not(IsTU()), IncludesAre({}),
136  HasDigest(digest(Level2HeaderCode))))));
137 }
138 
139 TEST_F(IndexActionTest, IncludeGraphSelfInclude) {
140  std::string MainFilePath = testPath("main.cpp");
141  std::string MainCode = "#include \"header.h\"";
142  std::string HeaderPath = testPath("header.h");
143  std::string HeaderCode = R"cpp(
144  #ifndef _GUARD_
145  #define _GUARD_
146  #include "header.h"
147  #endif)cpp";
148 
149  addFile(MainFilePath, MainCode);
150  addFile(HeaderPath, HeaderCode);
151 
152  IndexFileIn IndexFile = runIndexingAction(MainFilePath);
153  auto Nodes = toMap(*IndexFile.Sources);
154 
155  EXPECT_THAT(
156  Nodes,
157  UnorderedElementsAre(
158  Pair(toUri(MainFilePath), AllOf(IsTU(), IncludesAre({HeaderPath}),
159  HasDigest(digest(MainCode)))),
160  Pair(toUri(HeaderPath), AllOf(Not(IsTU()), IncludesAre({HeaderPath}),
161  HasDigest(digest(HeaderCode))))));
162 }
163 
164 TEST_F(IndexActionTest, IncludeGraphSkippedFile) {
165  std::string MainFilePath = testPath("main.cpp");
166  std::string MainCode = R"cpp(
167  #include "common.h"
168  #include "header.h"
169  )cpp";
170 
171  std::string CommonHeaderPath = testPath("common.h");
172  std::string CommonHeaderCode = R"cpp(
173  #ifndef _GUARD_
174  #define _GUARD_
175  void f();
176  #endif)cpp";
177 
178  std::string HeaderPath = testPath("header.h");
179  std::string HeaderCode = R"cpp(
180  #include "common.h"
181  void g();)cpp";
182 
183  addFile(MainFilePath, MainCode);
184  addFile(HeaderPath, HeaderCode);
185  addFile(CommonHeaderPath, CommonHeaderCode);
186 
187  IndexFileIn IndexFile = runIndexingAction(MainFilePath);
188  auto Nodes = toMap(*IndexFile.Sources);
189 
190  EXPECT_THAT(
191  Nodes, UnorderedElementsAre(
192  Pair(toUri(MainFilePath),
193  AllOf(IsTU(), IncludesAre({HeaderPath, CommonHeaderPath}),
194  HasDigest(digest(MainCode)))),
195  Pair(toUri(HeaderPath),
196  AllOf(Not(IsTU()), IncludesAre({CommonHeaderPath}),
197  HasDigest(digest(HeaderCode)))),
198  Pair(toUri(CommonHeaderPath),
199  AllOf(Not(IsTU()), IncludesAre({}),
200  HasDigest(digest(CommonHeaderCode))))));
201 }
202 
203 TEST_F(IndexActionTest, IncludeGraphDynamicInclude) {
204  std::string MainFilePath = testPath("main.cpp");
205  std::string MainCode = R"cpp(
206  #ifndef FOO
207  #define FOO "main.cpp"
208  #else
209  #define FOO "header.h"
210  #endif
211 
212  #include FOO)cpp";
213  std::string HeaderPath = testPath("header.h");
214  std::string HeaderCode = "";
215 
216  addFile(MainFilePath, MainCode);
217  addFile(HeaderPath, HeaderCode);
218 
219  IndexFileIn IndexFile = runIndexingAction(MainFilePath);
220  auto Nodes = toMap(*IndexFile.Sources);
221 
222  EXPECT_THAT(
223  Nodes,
224  UnorderedElementsAre(
225  Pair(toUri(MainFilePath),
226  AllOf(IsTU(), IncludesAre({MainFilePath, HeaderPath}),
227  HasDigest(digest(MainCode)))),
228  Pair(toUri(HeaderPath), AllOf(Not(IsTU()), IncludesAre({}),
229  HasDigest(digest(HeaderCode))))));
230 }
231 
232 TEST_F(IndexActionTest, NoWarnings) {
233  std::string MainFilePath = testPath("main.cpp");
234  std::string MainCode = R"cpp(
235  void foo(int x) {
236  if (x = 1) // -Wparentheses
237  return;
238  if (x = 1) // -Wparentheses
239  return;
240  }
241  void bar() {}
242  )cpp";
243  addFile(MainFilePath, MainCode);
244  // We set -ferror-limit so the warning-promoted-to-error would be fatal.
245  // This would cause indexing to stop (if warnings weren't disabled).
246  IndexFileIn IndexFile = runIndexingAction(
247  MainFilePath, {"-ferror-limit=1", "-Wparentheses", "-Werror"});
248  ASSERT_TRUE(IndexFile.Sources);
249  ASSERT_NE(0u, IndexFile.Sources->size());
250  EXPECT_THAT(*IndexFile.Symbols, ElementsAre(HasName("foo"), HasName("bar")));
251 }
252 
253 } // namespace
254 } // namespace clangd
255 } // namespace clang
MATCHER_P(Named, N, "")
Some operations such as code completion produce a set of candidates.
llvm::IntrusiveRefCntPtr< llvm::vfs::InMemoryFileSystem > InMemoryFileSystem
std::vector< llvm::StringRef > DirectIncludes
Definition: Headers.h:76
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
llvm::StringMap< IncludeGraphNode > IncludeGraph
Definition: Headers.h:82
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
llvm::unique_function< void()> Action
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
MATCHER(Declared, "")
static constexpr llvm::StringLiteral Name
const char * testRoot()
Definition: TestFS.cpp:74
std::vector< std::string > FilePaths
FileDigest digest(llvm::StringRef Content)
Definition: SourceCode.cpp:675
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:197
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::unique_ptr< FrontendAction > createStaticIndexingAction(SymbolCollector::Options Opts, std::function< void(SymbolSlab)> SymbolsCallback, std::function< void(RefSlab)> RefsCallback, std::function< void(RelationSlab)> RelationsCallback, std::function< void(IncludeGraph)> IncludeGraphCallback)
llvm::StringMap< std::string > Files