annotate clang-tools-extra/clangd/unittests/HeaderSourceSwitchTests.cpp @ 150:1d019706d866

LLVM10
author anatofuz
date Thu, 13 Feb 2020 15:10:13 +0900
parents
children 0572611fdcc8
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 //===--- HeaderSourceSwitchTests.cpp - ---------------------------*- C++-*-===//
anatofuz
parents:
diff changeset
2 //
anatofuz
parents:
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
anatofuz
parents:
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
anatofuz
parents:
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
anatofuz
parents:
diff changeset
6 //
anatofuz
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
8
anatofuz
parents:
diff changeset
9 #include "HeaderSourceSwitch.h"
anatofuz
parents:
diff changeset
10
anatofuz
parents:
diff changeset
11 #include "SyncAPI.h"
anatofuz
parents:
diff changeset
12 #include "TestFS.h"
anatofuz
parents:
diff changeset
13 #include "TestTU.h"
anatofuz
parents:
diff changeset
14 #include "index/MemIndex.h"
anatofuz
parents:
diff changeset
15 #include "gmock/gmock.h"
anatofuz
parents:
diff changeset
16 #include "gtest/gtest.h"
anatofuz
parents:
diff changeset
17
anatofuz
parents:
diff changeset
18 namespace clang {
anatofuz
parents:
diff changeset
19 namespace clangd {
anatofuz
parents:
diff changeset
20 namespace {
anatofuz
parents:
diff changeset
21
anatofuz
parents:
diff changeset
22 TEST(HeaderSourceSwitchTest, FileHeuristic) {
anatofuz
parents:
diff changeset
23 MockFSProvider FS;
anatofuz
parents:
diff changeset
24 auto FooCpp = testPath("foo.cpp");
anatofuz
parents:
diff changeset
25 auto FooH = testPath("foo.h");
anatofuz
parents:
diff changeset
26 auto Invalid = testPath("main.cpp");
anatofuz
parents:
diff changeset
27
anatofuz
parents:
diff changeset
28 FS.Files[FooCpp];
anatofuz
parents:
diff changeset
29 FS.Files[FooH];
anatofuz
parents:
diff changeset
30 FS.Files[Invalid];
anatofuz
parents:
diff changeset
31 Optional<Path> PathResult =
anatofuz
parents:
diff changeset
32 getCorrespondingHeaderOrSource(FooCpp, FS.getFileSystem());
anatofuz
parents:
diff changeset
33 EXPECT_TRUE(PathResult.hasValue());
anatofuz
parents:
diff changeset
34 ASSERT_EQ(PathResult.getValue(), FooH);
anatofuz
parents:
diff changeset
35
anatofuz
parents:
diff changeset
36 PathResult = getCorrespondingHeaderOrSource(FooH, FS.getFileSystem());
anatofuz
parents:
diff changeset
37 EXPECT_TRUE(PathResult.hasValue());
anatofuz
parents:
diff changeset
38 ASSERT_EQ(PathResult.getValue(), FooCpp);
anatofuz
parents:
diff changeset
39
anatofuz
parents:
diff changeset
40 // Test with header file in capital letters and different extension, source
anatofuz
parents:
diff changeset
41 // file with different extension
anatofuz
parents:
diff changeset
42 auto FooC = testPath("bar.c");
anatofuz
parents:
diff changeset
43 auto FooHH = testPath("bar.HH");
anatofuz
parents:
diff changeset
44
anatofuz
parents:
diff changeset
45 FS.Files[FooC];
anatofuz
parents:
diff changeset
46 FS.Files[FooHH];
anatofuz
parents:
diff changeset
47 PathResult = getCorrespondingHeaderOrSource(FooC, FS.getFileSystem());
anatofuz
parents:
diff changeset
48 EXPECT_TRUE(PathResult.hasValue());
anatofuz
parents:
diff changeset
49 ASSERT_EQ(PathResult.getValue(), FooHH);
anatofuz
parents:
diff changeset
50
anatofuz
parents:
diff changeset
51 // Test with both capital letters
anatofuz
parents:
diff changeset
52 auto Foo2C = testPath("foo2.C");
anatofuz
parents:
diff changeset
53 auto Foo2HH = testPath("foo2.HH");
anatofuz
parents:
diff changeset
54 FS.Files[Foo2C];
anatofuz
parents:
diff changeset
55 FS.Files[Foo2HH];
anatofuz
parents:
diff changeset
56 PathResult = getCorrespondingHeaderOrSource(Foo2C, FS.getFileSystem());
anatofuz
parents:
diff changeset
57 EXPECT_TRUE(PathResult.hasValue());
anatofuz
parents:
diff changeset
58 ASSERT_EQ(PathResult.getValue(), Foo2HH);
anatofuz
parents:
diff changeset
59
anatofuz
parents:
diff changeset
60 // Test with source file as capital letter and .hxx header file
anatofuz
parents:
diff changeset
61 auto Foo3C = testPath("foo3.C");
anatofuz
parents:
diff changeset
62 auto Foo3HXX = testPath("foo3.hxx");
anatofuz
parents:
diff changeset
63
anatofuz
parents:
diff changeset
64 FS.Files[Foo3C];
anatofuz
parents:
diff changeset
65 FS.Files[Foo3HXX];
anatofuz
parents:
diff changeset
66 PathResult = getCorrespondingHeaderOrSource(Foo3C, FS.getFileSystem());
anatofuz
parents:
diff changeset
67 EXPECT_TRUE(PathResult.hasValue());
anatofuz
parents:
diff changeset
68 ASSERT_EQ(PathResult.getValue(), Foo3HXX);
anatofuz
parents:
diff changeset
69
anatofuz
parents:
diff changeset
70 // Test if asking for a corresponding file that doesn't exist returns an empty
anatofuz
parents:
diff changeset
71 // string.
anatofuz
parents:
diff changeset
72 PathResult = getCorrespondingHeaderOrSource(Invalid, FS.getFileSystem());
anatofuz
parents:
diff changeset
73 EXPECT_FALSE(PathResult.hasValue());
anatofuz
parents:
diff changeset
74 }
anatofuz
parents:
diff changeset
75
anatofuz
parents:
diff changeset
76 MATCHER_P(DeclNamed, Name, "") {
anatofuz
parents:
diff changeset
77 if (const NamedDecl *ND = dyn_cast<NamedDecl>(arg))
anatofuz
parents:
diff changeset
78 if (ND->getQualifiedNameAsString() == Name)
anatofuz
parents:
diff changeset
79 return true;
anatofuz
parents:
diff changeset
80 return false;
anatofuz
parents:
diff changeset
81 }
anatofuz
parents:
diff changeset
82
anatofuz
parents:
diff changeset
83 TEST(HeaderSourceSwitchTest, GetLocalDecls) {
anatofuz
parents:
diff changeset
84 TestTU TU;
anatofuz
parents:
diff changeset
85 TU.HeaderCode = R"cpp(
anatofuz
parents:
diff changeset
86 void HeaderOnly();
anatofuz
parents:
diff changeset
87 )cpp";
anatofuz
parents:
diff changeset
88 TU.Code = R"cpp(
anatofuz
parents:
diff changeset
89 void MainF1();
anatofuz
parents:
diff changeset
90 class Foo {};
anatofuz
parents:
diff changeset
91 namespace ns {
anatofuz
parents:
diff changeset
92 class Foo {
anatofuz
parents:
diff changeset
93 void method();
anatofuz
parents:
diff changeset
94 int field;
anatofuz
parents:
diff changeset
95 };
anatofuz
parents:
diff changeset
96 } // namespace ns
anatofuz
parents:
diff changeset
97
anatofuz
parents:
diff changeset
98 // Non-indexable symbols
anatofuz
parents:
diff changeset
99 namespace {
anatofuz
parents:
diff changeset
100 void Ignore1() {}
anatofuz
parents:
diff changeset
101 }
anatofuz
parents:
diff changeset
102
anatofuz
parents:
diff changeset
103 )cpp";
anatofuz
parents:
diff changeset
104
anatofuz
parents:
diff changeset
105 auto AST = TU.build();
anatofuz
parents:
diff changeset
106 EXPECT_THAT(getIndexableLocalDecls(AST),
anatofuz
parents:
diff changeset
107 testing::UnorderedElementsAre(
anatofuz
parents:
diff changeset
108 DeclNamed("MainF1"), DeclNamed("Foo"), DeclNamed("ns::Foo"),
anatofuz
parents:
diff changeset
109 DeclNamed("ns::Foo::method"), DeclNamed("ns::Foo::field")));
anatofuz
parents:
diff changeset
110 }
anatofuz
parents:
diff changeset
111
anatofuz
parents:
diff changeset
112 TEST(HeaderSourceSwitchTest, FromHeaderToSource) {
anatofuz
parents:
diff changeset
113 // build a proper index, which contains symbols:
anatofuz
parents:
diff changeset
114 // A_Sym1, declared in TestTU.h, defined in a.cpp
anatofuz
parents:
diff changeset
115 // B_Sym[1-2], declared in TestTU.h, defined in b.cpp
anatofuz
parents:
diff changeset
116 SymbolSlab::Builder AllSymbols;
anatofuz
parents:
diff changeset
117 TestTU Testing;
anatofuz
parents:
diff changeset
118 Testing.HeaderFilename = "TestTU.h";
anatofuz
parents:
diff changeset
119 Testing.HeaderCode = "void A_Sym1();";
anatofuz
parents:
diff changeset
120 Testing.Filename = "a.cpp";
anatofuz
parents:
diff changeset
121 Testing.Code = "void A_Sym1() {};";
anatofuz
parents:
diff changeset
122 for (auto &Sym : Testing.headerSymbols())
anatofuz
parents:
diff changeset
123 AllSymbols.insert(Sym);
anatofuz
parents:
diff changeset
124
anatofuz
parents:
diff changeset
125 Testing.HeaderCode = R"cpp(
anatofuz
parents:
diff changeset
126 void B_Sym1();
anatofuz
parents:
diff changeset
127 void B_Sym2();
anatofuz
parents:
diff changeset
128 void B_Sym3_NoDef();
anatofuz
parents:
diff changeset
129 )cpp";
anatofuz
parents:
diff changeset
130 Testing.Filename = "b.cpp";
anatofuz
parents:
diff changeset
131 Testing.Code = R"cpp(
anatofuz
parents:
diff changeset
132 void B_Sym1() {}
anatofuz
parents:
diff changeset
133 void B_Sym2() {}
anatofuz
parents:
diff changeset
134 )cpp";
anatofuz
parents:
diff changeset
135 for (auto &Sym : Testing.headerSymbols())
anatofuz
parents:
diff changeset
136 AllSymbols.insert(Sym);
anatofuz
parents:
diff changeset
137 auto Index = MemIndex::build(std::move(AllSymbols).build(), {}, {});
anatofuz
parents:
diff changeset
138
anatofuz
parents:
diff changeset
139 // Test for swtich from .h header to .cc source
anatofuz
parents:
diff changeset
140 struct {
anatofuz
parents:
diff changeset
141 llvm::StringRef HeaderCode;
anatofuz
parents:
diff changeset
142 llvm::Optional<std::string> ExpectedSource;
anatofuz
parents:
diff changeset
143 } TestCases[] = {
anatofuz
parents:
diff changeset
144 {"// empty, no header found", llvm::None},
anatofuz
parents:
diff changeset
145 {R"cpp(
anatofuz
parents:
diff changeset
146 // no definition found in the index.
anatofuz
parents:
diff changeset
147 void NonDefinition();
anatofuz
parents:
diff changeset
148 )cpp",
anatofuz
parents:
diff changeset
149 llvm::None},
anatofuz
parents:
diff changeset
150 {R"cpp(
anatofuz
parents:
diff changeset
151 void A_Sym1();
anatofuz
parents:
diff changeset
152 )cpp",
anatofuz
parents:
diff changeset
153 testPath("a.cpp")},
anatofuz
parents:
diff changeset
154 {R"cpp(
anatofuz
parents:
diff changeset
155 // b.cpp wins.
anatofuz
parents:
diff changeset
156 void A_Sym1();
anatofuz
parents:
diff changeset
157 void B_Sym1();
anatofuz
parents:
diff changeset
158 void B_Sym2();
anatofuz
parents:
diff changeset
159 )cpp",
anatofuz
parents:
diff changeset
160 testPath("b.cpp")},
anatofuz
parents:
diff changeset
161 {R"cpp(
anatofuz
parents:
diff changeset
162 // a.cpp and b.cpp have same scope, but a.cpp because "a.cpp" < "b.cpp".
anatofuz
parents:
diff changeset
163 void A_Sym1();
anatofuz
parents:
diff changeset
164 void B_Sym1();
anatofuz
parents:
diff changeset
165 )cpp",
anatofuz
parents:
diff changeset
166 testPath("a.cpp")},
anatofuz
parents:
diff changeset
167
anatofuz
parents:
diff changeset
168 {R"cpp(
anatofuz
parents:
diff changeset
169 // We don't have definition in the index, so stay in the header.
anatofuz
parents:
diff changeset
170 void B_Sym3_NoDef();
anatofuz
parents:
diff changeset
171 )cpp",
anatofuz
parents:
diff changeset
172 None},
anatofuz
parents:
diff changeset
173 };
anatofuz
parents:
diff changeset
174 for (const auto &Case : TestCases) {
anatofuz
parents:
diff changeset
175 TestTU TU = TestTU::withCode(Case.HeaderCode);
anatofuz
parents:
diff changeset
176 TU.Filename = "TestTU.h";
anatofuz
parents:
diff changeset
177 TU.ExtraArgs.push_back("-xc++-header"); // inform clang this is a header.
anatofuz
parents:
diff changeset
178 auto HeaderAST = TU.build();
anatofuz
parents:
diff changeset
179 EXPECT_EQ(Case.ExpectedSource,
anatofuz
parents:
diff changeset
180 getCorrespondingHeaderOrSource(testPath(TU.Filename), HeaderAST,
anatofuz
parents:
diff changeset
181 Index.get()));
anatofuz
parents:
diff changeset
182 }
anatofuz
parents:
diff changeset
183 }
anatofuz
parents:
diff changeset
184
anatofuz
parents:
diff changeset
185 TEST(HeaderSourceSwitchTest, FromSourceToHeader) {
anatofuz
parents:
diff changeset
186 // build a proper index, which contains symbols:
anatofuz
parents:
diff changeset
187 // A_Sym1, declared in a.h, defined in TestTU.cpp
anatofuz
parents:
diff changeset
188 // B_Sym[1-2], declared in b.h, defined in TestTU.cpp
anatofuz
parents:
diff changeset
189 TestTU TUForIndex = TestTU::withCode(R"cpp(
anatofuz
parents:
diff changeset
190 #include "a.h"
anatofuz
parents:
diff changeset
191 #include "b.h"
anatofuz
parents:
diff changeset
192
anatofuz
parents:
diff changeset
193 void A_Sym1() {}
anatofuz
parents:
diff changeset
194
anatofuz
parents:
diff changeset
195 void B_Sym1() {}
anatofuz
parents:
diff changeset
196 void B_Sym2() {}
anatofuz
parents:
diff changeset
197 )cpp");
anatofuz
parents:
diff changeset
198 TUForIndex.AdditionalFiles["a.h"] = R"cpp(
anatofuz
parents:
diff changeset
199 void A_Sym1();
anatofuz
parents:
diff changeset
200 )cpp";
anatofuz
parents:
diff changeset
201 TUForIndex.AdditionalFiles["b.h"] = R"cpp(
anatofuz
parents:
diff changeset
202 void B_Sym1();
anatofuz
parents:
diff changeset
203 void B_Sym2();
anatofuz
parents:
diff changeset
204 )cpp";
anatofuz
parents:
diff changeset
205 TUForIndex.Filename = "TestTU.cpp";
anatofuz
parents:
diff changeset
206 auto Index = TUForIndex.index();
anatofuz
parents:
diff changeset
207
anatofuz
parents:
diff changeset
208 // Test for switching from .cc source file to .h header.
anatofuz
parents:
diff changeset
209 struct {
anatofuz
parents:
diff changeset
210 llvm::StringRef SourceCode;
anatofuz
parents:
diff changeset
211 llvm::Optional<std::string> ExpectedResult;
anatofuz
parents:
diff changeset
212 } TestCases[] = {
anatofuz
parents:
diff changeset
213 {"// empty, no header found", llvm::None},
anatofuz
parents:
diff changeset
214 {R"cpp(
anatofuz
parents:
diff changeset
215 // symbol not in index, no header found
anatofuz
parents:
diff changeset
216 void Local() {}
anatofuz
parents:
diff changeset
217 )cpp",
anatofuz
parents:
diff changeset
218 llvm::None},
anatofuz
parents:
diff changeset
219
anatofuz
parents:
diff changeset
220 {R"cpp(
anatofuz
parents:
diff changeset
221 // a.h wins.
anatofuz
parents:
diff changeset
222 void A_Sym1() {}
anatofuz
parents:
diff changeset
223 )cpp",
anatofuz
parents:
diff changeset
224 testPath("a.h")},
anatofuz
parents:
diff changeset
225
anatofuz
parents:
diff changeset
226 {R"cpp(
anatofuz
parents:
diff changeset
227 // b.h wins.
anatofuz
parents:
diff changeset
228 void A_Sym1() {}
anatofuz
parents:
diff changeset
229 void B_Sym1() {}
anatofuz
parents:
diff changeset
230 void B_Sym2() {}
anatofuz
parents:
diff changeset
231 )cpp",
anatofuz
parents:
diff changeset
232 testPath("b.h")},
anatofuz
parents:
diff changeset
233
anatofuz
parents:
diff changeset
234 {R"cpp(
anatofuz
parents:
diff changeset
235 // a.h and b.h have same scope, but a.h wins because "a.h" < "b.h".
anatofuz
parents:
diff changeset
236 void A_Sym1() {}
anatofuz
parents:
diff changeset
237 void B_Sym1() {}
anatofuz
parents:
diff changeset
238 )cpp",
anatofuz
parents:
diff changeset
239 testPath("a.h")},
anatofuz
parents:
diff changeset
240 };
anatofuz
parents:
diff changeset
241 for (const auto &Case : TestCases) {
anatofuz
parents:
diff changeset
242 TestTU TU = TestTU::withCode(Case.SourceCode);
anatofuz
parents:
diff changeset
243 TU.Filename = "Test.cpp";
anatofuz
parents:
diff changeset
244 auto AST = TU.build();
anatofuz
parents:
diff changeset
245 EXPECT_EQ(Case.ExpectedResult,
anatofuz
parents:
diff changeset
246 getCorrespondingHeaderOrSource(testPath(TU.Filename), AST,
anatofuz
parents:
diff changeset
247 Index.get()));
anatofuz
parents:
diff changeset
248 }
anatofuz
parents:
diff changeset
249 }
anatofuz
parents:
diff changeset
250
anatofuz
parents:
diff changeset
251 TEST(HeaderSourceSwitchTest, ClangdServerIntegration) {
anatofuz
parents:
diff changeset
252 MockCompilationDatabase CDB;
anatofuz
parents:
diff changeset
253 CDB.ExtraClangFlags = {"-I" +
anatofuz
parents:
diff changeset
254 testPath("src/include")}; // add search directory.
anatofuz
parents:
diff changeset
255 MockFSProvider FS;
anatofuz
parents:
diff changeset
256 // File heuristic fails here, we rely on the index to find the .h file.
anatofuz
parents:
diff changeset
257 std::string CppPath = testPath("src/lib/test.cpp");
anatofuz
parents:
diff changeset
258 std::string HeaderPath = testPath("src/include/test.h");
anatofuz
parents:
diff changeset
259 FS.Files[HeaderPath] = "void foo();";
anatofuz
parents:
diff changeset
260 const std::string FileContent = R"cpp(
anatofuz
parents:
diff changeset
261 #include "test.h"
anatofuz
parents:
diff changeset
262 void foo() {};
anatofuz
parents:
diff changeset
263 )cpp";
anatofuz
parents:
diff changeset
264 FS.Files[CppPath] = FileContent;
anatofuz
parents:
diff changeset
265 auto Options = ClangdServer::optsForTest();
anatofuz
parents:
diff changeset
266 Options.BuildDynamicSymbolIndex = true;
anatofuz
parents:
diff changeset
267 ClangdServer Server(CDB, FS, Options);
anatofuz
parents:
diff changeset
268 runAddDocument(Server, CppPath, FileContent);
anatofuz
parents:
diff changeset
269 EXPECT_EQ(HeaderPath,
anatofuz
parents:
diff changeset
270 *llvm::cantFail(runSwitchHeaderSource(Server, CppPath)));
anatofuz
parents:
diff changeset
271 }
anatofuz
parents:
diff changeset
272
anatofuz
parents:
diff changeset
273 } // namespace
anatofuz
parents:
diff changeset
274 } // namespace clangd
anatofuz
parents:
diff changeset
275 } // namespace clang