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