clang-tools  10.0.0git
HeaderSourceSwitchTests.cpp
Go to the documentation of this file.
1 //===--- HeaderSourceSwitchTests.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 "HeaderSourceSwitch.h"
10 
11 #include "SyncAPI.h"
12 #include "TestFS.h"
13 #include "TestTU.h"
14 #include "index/MemIndex.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17 
18 namespace clang {
19 namespace clangd {
20 namespace {
21 
22 TEST(HeaderSourceSwitchTest, FileHeuristic) {
23  MockFSProvider FS;
24  auto FooCpp = testPath("foo.cpp");
25  auto FooH = testPath("foo.h");
26  auto Invalid = testPath("main.cpp");
27 
28  FS.Files[FooCpp];
29  FS.Files[FooH];
30  FS.Files[Invalid];
31  Optional<Path> PathResult =
32  getCorrespondingHeaderOrSource(FooCpp, FS.getFileSystem());
33  EXPECT_TRUE(PathResult.hasValue());
34  ASSERT_EQ(PathResult.getValue(), FooH);
35 
36  PathResult = getCorrespondingHeaderOrSource(FooH, FS.getFileSystem());
37  EXPECT_TRUE(PathResult.hasValue());
38  ASSERT_EQ(PathResult.getValue(), FooCpp);
39 
40  // Test with header file in capital letters and different extension, source
41  // file with different extension
42  auto FooC = testPath("bar.c");
43  auto FooHH = testPath("bar.HH");
44 
45  FS.Files[FooC];
46  FS.Files[FooHH];
47  PathResult = getCorrespondingHeaderOrSource(FooC, FS.getFileSystem());
48  EXPECT_TRUE(PathResult.hasValue());
49  ASSERT_EQ(PathResult.getValue(), FooHH);
50 
51  // Test with both capital letters
52  auto Foo2C = testPath("foo2.C");
53  auto Foo2HH = testPath("foo2.HH");
54  FS.Files[Foo2C];
55  FS.Files[Foo2HH];
56  PathResult = getCorrespondingHeaderOrSource(Foo2C, FS.getFileSystem());
57  EXPECT_TRUE(PathResult.hasValue());
58  ASSERT_EQ(PathResult.getValue(), Foo2HH);
59 
60  // Test with source file as capital letter and .hxx header file
61  auto Foo3C = testPath("foo3.C");
62  auto Foo3HXX = testPath("foo3.hxx");
63 
64  FS.Files[Foo3C];
65  FS.Files[Foo3HXX];
66  PathResult = getCorrespondingHeaderOrSource(Foo3C, FS.getFileSystem());
67  EXPECT_TRUE(PathResult.hasValue());
68  ASSERT_EQ(PathResult.getValue(), Foo3HXX);
69 
70  // Test if asking for a corresponding file that doesn't exist returns an empty
71  // string.
72  PathResult = getCorrespondingHeaderOrSource(Invalid, FS.getFileSystem());
73  EXPECT_FALSE(PathResult.hasValue());
74 }
75 
76 MATCHER_P(DeclNamed, Name, "") {
77  if (const NamedDecl *ND = dyn_cast<NamedDecl>(arg))
78  if (ND->getQualifiedNameAsString() == Name)
79  return true;
80  return false;
81 }
82 
83 TEST(HeaderSourceSwitchTest, GetLocalDecls) {
84  TestTU TU;
85  TU.HeaderCode = R"cpp(
86  void HeaderOnly();
87  )cpp";
88  TU.Code = R"cpp(
89  void MainF1();
90  class Foo {};
91  namespace ns {
92  class Foo {
93  void method();
94  int field;
95  };
96  } // namespace ns
97 
98  // Non-indexable symbols
99  namespace {
100  void Ignore1() {}
101  }
102 
103  )cpp";
104 
105  auto AST = TU.build();
106  EXPECT_THAT(getIndexableLocalDecls(AST),
107  testing::UnorderedElementsAre(
108  DeclNamed("MainF1"), DeclNamed("Foo"), DeclNamed("ns::Foo"),
109  DeclNamed("ns::Foo::method"), DeclNamed("ns::Foo::field")));
110 }
111 
112 TEST(HeaderSourceSwitchTest, FromHeaderToSource) {
113  // build a proper index, which contains symbols:
114  // A_Sym1, declared in TestTU.h, defined in a.cpp
115  // B_Sym[1-2], declared in TestTU.h, defined in b.cpp
116  SymbolSlab::Builder AllSymbols;
117  TestTU Testing;
118  Testing.HeaderFilename = "TestTU.h";
119  Testing.HeaderCode = "void A_Sym1();";
120  Testing.Filename = "a.cpp";
121  Testing.Code = "void A_Sym1() {};";
122  for (auto &Sym : Testing.headerSymbols())
123  AllSymbols.insert(Sym);
124 
125  Testing.HeaderCode = R"cpp(
126  void B_Sym1();
127  void B_Sym2();
128  void B_Sym3_NoDef();
129  )cpp";
130  Testing.Filename = "b.cpp";
131  Testing.Code = R"cpp(
132  void B_Sym1() {}
133  void B_Sym2() {}
134  )cpp";
135  for (auto &Sym : Testing.headerSymbols())
136  AllSymbols.insert(Sym);
137  auto Index = MemIndex::build(std::move(AllSymbols).build(), {}, {});
138 
139  // Test for swtich from .h header to .cc source
140  struct {
141  llvm::StringRef HeaderCode;
142  llvm::Optional<std::string> ExpectedSource;
143  } TestCases[] = {
144  {"// empty, no header found", llvm::None},
145  {R"cpp(
146  // no definition found in the index.
147  void NonDefinition();
148  )cpp",
149  llvm::None},
150  {R"cpp(
151  void A_Sym1();
152  )cpp",
153  testPath("a.cpp")},
154  {R"cpp(
155  // b.cpp wins.
156  void A_Sym1();
157  void B_Sym1();
158  void B_Sym2();
159  )cpp",
160  testPath("b.cpp")},
161  {R"cpp(
162  // a.cpp and b.cpp have same scope, but a.cpp because "a.cpp" < "b.cpp".
163  void A_Sym1();
164  void B_Sym1();
165  )cpp",
166  testPath("a.cpp")},
167 
168  {R"cpp(
169  // We don't have definition in the index, so stay in the header.
170  void B_Sym3_NoDef();
171  )cpp",
172  None},
173  };
174  for (const auto &Case : TestCases) {
175  TestTU TU = TestTU::withCode(Case.HeaderCode);
176  TU.Filename = "TestTU.h";
177  TU.ExtraArgs.push_back("-xc++-header"); // inform clang this is a header.
178  auto HeaderAST = TU.build();
179  EXPECT_EQ(Case.ExpectedSource,
180  getCorrespondingHeaderOrSource(testPath(TU.Filename), HeaderAST,
181  Index.get()));
182  }
183 }
184 
185 TEST(HeaderSourceSwitchTest, FromSourceToHeader) {
186  // build a proper index, which contains symbols:
187  // A_Sym1, declared in a.h, defined in TestTU.cpp
188  // B_Sym[1-2], declared in b.h, defined in TestTU.cpp
189  TestTU TUForIndex = TestTU::withCode(R"cpp(
190  #include "a.h"
191  #include "b.h"
192 
193  void A_Sym1() {}
194 
195  void B_Sym1() {}
196  void B_Sym2() {}
197  )cpp");
198  TUForIndex.AdditionalFiles["a.h"] = R"cpp(
199  void A_Sym1();
200  )cpp";
201  TUForIndex.AdditionalFiles["b.h"] = R"cpp(
202  void B_Sym1();
203  void B_Sym2();
204  )cpp";
205  TUForIndex.Filename = "TestTU.cpp";
206  auto Index = TUForIndex.index();
207 
208  // Test for switching from .cc source file to .h header.
209  struct {
210  llvm::StringRef SourceCode;
211  llvm::Optional<std::string> ExpectedResult;
212  } TestCases[] = {
213  {"// empty, no header found", llvm::None},
214  {R"cpp(
215  // symbol not in index, no header found
216  void Local() {}
217  )cpp",
218  llvm::None},
219 
220  {R"cpp(
221  // a.h wins.
222  void A_Sym1() {}
223  )cpp",
224  testPath("a.h")},
225 
226  {R"cpp(
227  // b.h wins.
228  void A_Sym1() {}
229  void B_Sym1() {}
230  void B_Sym2() {}
231  )cpp",
232  testPath("b.h")},
233 
234  {R"cpp(
235  // a.h and b.h have same scope, but a.h wins because "a.h" < "b.h".
236  void A_Sym1() {}
237  void B_Sym1() {}
238  )cpp",
239  testPath("a.h")},
240  };
241  for (const auto &Case : TestCases) {
242  TestTU TU = TestTU::withCode(Case.SourceCode);
243  TU.Filename = "Test.cpp";
244  auto AST = TU.build();
245  EXPECT_EQ(Case.ExpectedResult,
246  getCorrespondingHeaderOrSource(testPath(TU.Filename), AST,
247  Index.get()));
248  }
249 }
250 
251 TEST(HeaderSourceSwitchTest, ClangdServerIntegration) {
252  class IgnoreDiagnostics : public DiagnosticsConsumer {
254  std::vector<Diag> Diagnostics) override {}
255  } DiagConsumer;
256  MockCompilationDatabase CDB;
257  CDB.ExtraClangFlags = {"-I" +
258  testPath("src/include")}; // add search directory.
259  MockFSProvider FS;
260  // File heuristic fails here, we rely on the index to find the .h file.
261  std::string CppPath = testPath("src/lib/test.cpp");
262  std::string HeaderPath = testPath("src/include/test.h");
263  FS.Files[HeaderPath] = "void foo();";
264  const std::string FileContent = R"cpp(
265  #include "test.h"
266  void foo() {};
267  )cpp";
268  FS.Files[CppPath] = FileContent;
269  auto Options = ClangdServer::optsForTest();
270  Options.BuildDynamicSymbolIndex = true;
271  ClangdServer Server(CDB, FS, DiagConsumer, Options);
272  runAddDocument(Server, CppPath, FileContent);
273  EXPECT_EQ(HeaderPath,
274  *llvm::cantFail(runSwitchHeaderSource(Server, CppPath)));
275 }
276 
277 } // namespace
278 } // namespace clangd
279 } // namespace clang
MATCHER_P(Named, N, "")
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
MockFSProvider FS
static Options optsForTest()
Documents should not be synced at all.
llvm::Expected< llvm::Optional< clangd::Path > > runSwitchHeaderSource(ClangdServer &Server, PathRef File)
Definition: SyncAPI.cpp:155
std::vector< const Decl * > getIndexableLocalDecls(ParsedAST &AST)
Returns all indexable decls that are present in the main file of the AST.
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
Definition: MemIndex.cpp:19
TEST(BackgroundQueueTest, Priority)
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
virtual void onDiagnosticsReady(PathRef File, std::vector< Diag > Diagnostics)=0
Called by ClangdServer when Diagnostics for File are ready.
static constexpr llvm::StringLiteral Name
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
CodeCompletionBuilder Builder
llvm::Optional< Path > getCorrespondingHeaderOrSource(const Path &OriginalFile, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS)
Given a header file, returns the best matching source file, and vice visa.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
ClangdServer Server
IgnoreDiagnostics DiagConsumer
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
Definition: ParsedAST.cpp:218
void runAddDocument(ClangdServer &Server, PathRef File, llvm::StringRef Contents, WantDiagnostics WantDiags)
Definition: SyncAPI.cpp:15
const SymbolIndex * Index
Definition: Dexp.cpp:84