clang-tools 10.0.0

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 {
253 void onDiagnosticsReady(PathRef File,
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
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:26
MemIndex.h
clang::clangd::PathRef
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
FS
MockFSProvider FS
Definition: HeadersTests.cpp:119
clang::clangd::ClangdServer::optsForTest
static Options optsForTest()
Definition: ClangdServer.cpp:103
clang::clangd::TextDocumentSyncKind::None
Documents should not be synced at all.
TestFS.h
TestTU.h
clang::clangd::runSwitchHeaderSource
llvm::Expected< llvm::Optional< clangd::Path > > runSwitchHeaderSource(ClangdServer &Server, PathRef File)
Definition: SyncAPI.cpp:155
clang::clangd::getIndexableLocalDecls
std::vector< const Decl * > getIndexableLocalDecls(ParsedAST &AST)
Returns all indexable decls that are present in the main file of the AST.
Definition: HeaderSourceSwitch.cpp:127
clang::clangd::MemIndex::build
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
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:647
clang::clangd::testPath
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
HeaderSourceSwitch.h
clang::clangd::DiagnosticsConsumer::onDiagnosticsReady
virtual void onDiagnosticsReady(PathRef File, std::vector< Diag > Diagnostics)=0
Called by ClangdServer when Diagnostics for File are ready.
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:35
clang::clangd::getCorrespondingHeaderOrSource
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.
Definition: HeaderSourceSwitch.cpp:19
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Server
ClangdServer Server
Definition: FindSymbolsTests.cpp:70
SyncAPI.h
DiagConsumer
IgnoreDiagnostics DiagConsumer
Definition: FindSymbolsTests.cpp:69
clang::clangd::ParsedAST::build
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
clang::clangd::CompletionItemKind::File
clang::clangd::runAddDocument
void runAddDocument(ClangdServer &Server, PathRef File, llvm::StringRef Contents, WantDiagnostics WantDiags)
Definition: SyncAPI.cpp:15
Index
const SymbolIndex * Index
Definition: Dexp.cpp:84