clang-tools  14.0.0git
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::EndsWith;
23 using ::testing::Not;
24 using ::testing::Pair;
25 using ::testing::UnorderedElementsAre;
26 using ::testing::UnorderedPointwise;
27 
28 std::string toUri(llvm::StringRef Path) { return URI::create(Path).toString(); }
29 
30 MATCHER(IsTU, "") { return arg.Flags & IncludeGraphNode::SourceFlag::IsTU; }
31 
32 MATCHER_P(HasDigest, Digest, "") { return arg.Digest == Digest; }
33 
34 MATCHER_P(HasName, Name, "") { return arg.Name == Name; }
35 
36 MATCHER(HasSameURI, "") {
37  llvm::StringRef URI = ::testing::get<0>(arg);
38  const std::string &Path = ::testing::get<1>(arg);
39  return toUri(Path) == URI;
40 }
41 
42 ::testing::Matcher<const IncludeGraphNode &>
43 IncludesAre(const std::vector<std::string> &Includes) {
44  return ::testing::Field(&IncludeGraphNode::DirectIncludes,
45  UnorderedPointwise(HasSameURI(), Includes));
46 }
47 
48 void checkNodesAreInitialized(const IndexFileIn &IndexFile,
49  const std::vector<std::string> &Paths) {
50  ASSERT_TRUE(IndexFile.Sources);
51  EXPECT_THAT(Paths.size(), IndexFile.Sources->size());
52  for (llvm::StringRef Path : Paths) {
53  auto URI = toUri(Path);
54  const auto &Node = IndexFile.Sources->lookup(URI);
55  // Uninitialized nodes will have an empty URI.
56  EXPECT_EQ(Node.URI.data(), IndexFile.Sources->find(URI)->getKeyData());
57  }
58 }
59 
60 std::map<std::string, const IncludeGraphNode &> toMap(const IncludeGraph &IG) {
61  std::map<std::string, const IncludeGraphNode &> Nodes;
62  for (auto &I : IG)
63  Nodes.emplace(std::string(I.getKey()), I.getValue());
64  return Nodes;
65 }
66 
67 class IndexActionTest : public ::testing::Test {
68 public:
69  IndexActionTest() : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem) {}
70 
71  IndexFileIn
72  runIndexingAction(llvm::StringRef MainFilePath,
73  const std::vector<std::string> &ExtraArgs = {}) {
74  IndexFileIn IndexFile;
75  llvm::IntrusiveRefCntPtr<FileManager> Files(
76  new FileManager(FileSystemOptions(), InMemoryFileSystem));
77 
79  Opts, [&](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(std::string(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::getMemBufferCopy(Content));
103  FilePaths.push_back(std::string(Path));
104  }
105 
106 protected:
107  SymbolCollector::Options Opts;
108  std::vector<std::string> FilePaths;
109  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem;
110 };
111 
112 TEST_F(IndexActionTest, CollectIncludeGraph) {
113  std::string MainFilePath = testPath("main.cpp");
114  std::string MainCode = "#include \"level1.h\"";
115  std::string Level1HeaderPath = testPath("level1.h");
116  std::string Level1HeaderCode = "#include \"level2.h\"";
117  std::string Level2HeaderPath = testPath("level2.h");
118  std::string Level2HeaderCode = "";
119 
120  addFile(MainFilePath, MainCode);
121  addFile(Level1HeaderPath, Level1HeaderCode);
122  addFile(Level2HeaderPath, Level2HeaderCode);
123 
124  IndexFileIn IndexFile = runIndexingAction(MainFilePath);
125  auto Nodes = toMap(*IndexFile.Sources);
126 
127  EXPECT_THAT(Nodes,
128  UnorderedElementsAre(
129  Pair(toUri(MainFilePath),
130  AllOf(IsTU(), IncludesAre({Level1HeaderPath}),
131  HasDigest(digest(MainCode)))),
132  Pair(toUri(Level1HeaderPath),
133  AllOf(Not(IsTU()), IncludesAre({Level2HeaderPath}),
134  HasDigest(digest(Level1HeaderCode)))),
135  Pair(toUri(Level2HeaderPath),
136  AllOf(Not(IsTU()), IncludesAre({}),
137  HasDigest(digest(Level2HeaderCode))))));
138 }
139 
140 TEST_F(IndexActionTest, IncludeGraphSelfInclude) {
141  std::string MainFilePath = testPath("main.cpp");
142  std::string MainCode = "#include \"header.h\"";
143  std::string HeaderPath = testPath("header.h");
144  std::string HeaderCode = R"cpp(
145  #ifndef _GUARD_
146  #define _GUARD_
147  #include "header.h"
148  #endif)cpp";
149 
150  addFile(MainFilePath, MainCode);
151  addFile(HeaderPath, HeaderCode);
152 
153  IndexFileIn IndexFile = runIndexingAction(MainFilePath);
154  auto Nodes = toMap(*IndexFile.Sources);
155 
156  EXPECT_THAT(
157  Nodes,
158  UnorderedElementsAre(
159  Pair(toUri(MainFilePath), AllOf(IsTU(), IncludesAre({HeaderPath}),
160  HasDigest(digest(MainCode)))),
161  Pair(toUri(HeaderPath), AllOf(Not(IsTU()), IncludesAre({HeaderPath}),
162  HasDigest(digest(HeaderCode))))));
163 }
164 
165 TEST_F(IndexActionTest, IncludeGraphSkippedFile) {
166  std::string MainFilePath = testPath("main.cpp");
167  std::string MainCode = R"cpp(
168  #include "common.h"
169  #include "header.h"
170  )cpp";
171 
172  std::string CommonHeaderPath = testPath("common.h");
173  std::string CommonHeaderCode = R"cpp(
174  #ifndef _GUARD_
175  #define _GUARD_
176  void f();
177  #endif)cpp";
178 
179  std::string HeaderPath = testPath("header.h");
180  std::string HeaderCode = R"cpp(
181  #include "common.h"
182  void g();)cpp";
183 
184  addFile(MainFilePath, MainCode);
185  addFile(HeaderPath, HeaderCode);
186  addFile(CommonHeaderPath, CommonHeaderCode);
187 
188  IndexFileIn IndexFile = runIndexingAction(MainFilePath);
189  auto Nodes = toMap(*IndexFile.Sources);
190 
191  EXPECT_THAT(
192  Nodes, UnorderedElementsAre(
193  Pair(toUri(MainFilePath),
194  AllOf(IsTU(), IncludesAre({HeaderPath, CommonHeaderPath}),
195  HasDigest(digest(MainCode)))),
196  Pair(toUri(HeaderPath),
197  AllOf(Not(IsTU()), IncludesAre({CommonHeaderPath}),
198  HasDigest(digest(HeaderCode)))),
199  Pair(toUri(CommonHeaderPath),
200  AllOf(Not(IsTU()), IncludesAre({}),
201  HasDigest(digest(CommonHeaderCode))))));
202 }
203 
204 TEST_F(IndexActionTest, IncludeGraphDynamicInclude) {
205  std::string MainFilePath = testPath("main.cpp");
206  std::string MainCode = R"cpp(
207  #ifndef FOO
208  #define FOO "main.cpp"
209  #else
210  #define FOO "header.h"
211  #endif
212 
213  #include FOO)cpp";
214  std::string HeaderPath = testPath("header.h");
215  std::string HeaderCode = "";
216 
217  addFile(MainFilePath, MainCode);
218  addFile(HeaderPath, HeaderCode);
219 
220  IndexFileIn IndexFile = runIndexingAction(MainFilePath);
221  auto Nodes = toMap(*IndexFile.Sources);
222 
223  EXPECT_THAT(
224  Nodes,
225  UnorderedElementsAre(
226  Pair(toUri(MainFilePath),
227  AllOf(IsTU(), IncludesAre({MainFilePath, HeaderPath}),
228  HasDigest(digest(MainCode)))),
229  Pair(toUri(HeaderPath), AllOf(Not(IsTU()), IncludesAre({}),
230  HasDigest(digest(HeaderCode))))));
231 }
232 
233 TEST_F(IndexActionTest, NoWarnings) {
234  std::string MainFilePath = testPath("main.cpp");
235  std::string MainCode = R"cpp(
236  void foo(int x) {
237  if (x = 1) // -Wparentheses
238  return;
239  if (x = 1) // -Wparentheses
240  return;
241  }
242  void bar() {}
243  )cpp";
244  addFile(MainFilePath, MainCode);
245  // We set -ferror-limit so the warning-promoted-to-error would be fatal.
246  // This would cause indexing to stop (if warnings weren't disabled).
247  IndexFileIn IndexFile = runIndexingAction(
248  MainFilePath, {"-ferror-limit=1", "-Wparentheses", "-Werror"});
249  ASSERT_TRUE(IndexFile.Sources);
250  ASSERT_NE(0u, IndexFile.Sources->size());
251  EXPECT_THAT(*IndexFile.Symbols, ElementsAre(HasName("foo"), HasName("bar")));
252 }
253 
254 TEST_F(IndexActionTest, SkipFiles) {
255  std::string MainFilePath = testPath("main.cpp");
256  addFile(MainFilePath, R"cpp(
257  // clang-format off
258  #include "good.h"
259  #include "bad.h"
260  // clang-format on
261  )cpp");
262  addFile(testPath("good.h"), R"cpp(
263  struct S { int s; };
264  void f1() { S f; }
265  auto unskippable1() { return S(); }
266  )cpp");
267  addFile(testPath("bad.h"), R"cpp(
268  struct T { S t; };
269  void f2() { S f; }
270  auto unskippable2() { return S(); }
271  )cpp");
272  Opts.FileFilter = [](const SourceManager &SM, FileID F) {
273  return !SM.getFileEntryForID(F)->getName().endswith("bad.h");
274  };
275  IndexFileIn IndexFile = runIndexingAction(MainFilePath, {"-std=c++14"});
276  EXPECT_THAT(*IndexFile.Symbols,
277  UnorderedElementsAre(HasName("S"), HasName("s"), HasName("f1"),
278  HasName("unskippable1")));
279  for (const auto &Pair : *IndexFile.Refs)
280  for (const auto &Ref : Pair.second)
281  EXPECT_THAT(Ref.Location.FileURI, EndsWith("good.h"));
282 }
283 
284 TEST_F(IndexActionTest, SkipNestedSymbols) {
285  std::string MainFilePath = testPath("main.cpp");
286  addFile(MainFilePath, R"cpp(
287  namespace ns1 {
288  namespace ns2 {
289  namespace ns3 {
290  namespace ns4 {
291  namespace ns5 {
292  namespace ns6 {
293  namespace ns7 {
294  namespace ns8 {
295  namespace ns9 {
296  class Bar {};
297  void foo() {
298  class Baz {};
299  }
300  }
301  }
302  }
303  }
304  }
305  }
306  }
307  }
308  })cpp");
309  IndexFileIn IndexFile = runIndexingAction(MainFilePath, {"-std=c++14"});
310  EXPECT_THAT(*IndexFile.Symbols, testing::Contains(HasName("foo")));
311  EXPECT_THAT(*IndexFile.Symbols, testing::Contains(HasName("Bar")));
312  EXPECT_THAT(*IndexFile.Symbols, Not(testing::Contains(HasName("Baz"))));
313 }
314 } // namespace
315 } // namespace clangd
316 } // namespace clang
llvm
Some operations such as code completion produce a set of candidates.
Definition: YAMLGenerator.cpp:28
Headers.h
IndexAction.h
clang::clangd::digest
FileDigest digest(llvm::StringRef Content)
Definition: SourceCode.cpp:562
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
clang::clangd::URI::create
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:211
clang::clangd::SymbolCollector::Options::FileFilter
std::function< bool(const SourceManager &, FileID)> FileFilter
If this is set, only collect symbols/references from a file if FileFilter(SM, FID) is true.
Definition: SymbolCollector.h:90
clang::clangd::IncludeGraphNode::SourceFlag::IsTU
@ IsTU
clang::clangd::TEST_F
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Definition: BackgroundIndexTests.cpp:94
Action
llvm::unique_function< void()> Action
Definition: TUScheduler.cpp:600
Opts
SymbolCollector::Options Opts
Definition: IndexActionTests.cpp:107
clang::clangd::testRoot
const char * testRoot()
Definition: TestFS.cpp:74
Args
llvm::json::Object Args
Definition: Trace.cpp:139
TestFS.h
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
clang::clangd::IncludeGraphNode::DirectIncludes
std::vector< llvm::StringRef > DirectIncludes
Definition: Headers.h:84
Files
llvm::DenseSet< FileID > Files
Definition: IncludeCleaner.cpp:106
FilePaths
std::vector< std::string > FilePaths
Definition: IndexActionTests.cpp:108
InMemoryFileSystem
llvm::IntrusiveRefCntPtr< llvm::vfs::InMemoryFileSystem > InMemoryFileSystem
Definition: IndexActionTests.cpp:109
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:31
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::IncludeGraph
llvm::StringMap< IncludeGraphNode > IncludeGraph
Definition: Headers.h:90
SM
const SourceManager & SM
Definition: IncludeCleaner.cpp:108
clang::clangd::createStaticIndexingAction
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)
Definition: IndexAction.cpp:214
clang::clangd::MATCHER
MATCHER(Declared, "")
Definition: BackgroundIndexTests.cpp:33