[AMDGPU][AsmParser][NFC] Translate parsed MIMG instructions to MCInsts automatically.
[llvm-project.git] / clang-tools-extra / clangd / unittests / CallHierarchyTests.cpp
blob6fa76aa6094bf262c06c09a626821c3173800ad7
1 //===-- CallHierarchyTests.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 #include "Annotations.h"
9 #include "ParsedAST.h"
10 #include "TestFS.h"
11 #include "TestTU.h"
12 #include "TestWorkspace.h"
13 #include "XRefs.h"
14 #include "llvm/Support/Path.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
18 namespace clang {
19 namespace clangd {
21 llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
22 const CallHierarchyItem &Item) {
23 return Stream << Item.name << "@" << Item.selectionRange;
26 llvm::raw_ostream &operator<<(llvm::raw_ostream &Stream,
27 const CallHierarchyIncomingCall &Call) {
28 Stream << "{ from: " << Call.from << ", ranges: [";
29 for (const auto &R : Call.fromRanges) {
30 Stream << R;
31 Stream << ", ";
33 return Stream << "] }";
36 namespace {
38 using ::testing::AllOf;
39 using ::testing::ElementsAre;
40 using ::testing::Field;
41 using ::testing::IsEmpty;
42 using ::testing::Matcher;
43 using ::testing::UnorderedElementsAre;
45 // Helpers for matching call hierarchy data structures.
46 MATCHER_P(withName, N, "") { return arg.name == N; }
47 MATCHER_P(withSelectionRange, R, "") { return arg.selectionRange == R; }
49 template <class ItemMatcher>
50 ::testing::Matcher<CallHierarchyIncomingCall> from(ItemMatcher M) {
51 return Field(&CallHierarchyIncomingCall::from, M);
53 template <class... RangeMatchers>
54 ::testing::Matcher<CallHierarchyIncomingCall> fromRanges(RangeMatchers... M) {
55 return Field(&CallHierarchyIncomingCall::fromRanges,
56 UnorderedElementsAre(M...));
59 TEST(CallHierarchy, IncomingOneFileCpp) {
60 Annotations Source(R"cpp(
61 void call^ee(int);
62 void caller1() {
63 $Callee[[callee]](42);
65 void caller2() {
66 $Caller1A[[caller1]]();
67 $Caller1B[[caller1]]();
69 void caller3() {
70 $Caller1C[[caller1]]();
71 $Caller2[[caller2]]();
73 )cpp");
74 TestTU TU = TestTU::withCode(Source.code());
75 auto AST = TU.build();
76 auto Index = TU.index();
78 std::vector<CallHierarchyItem> Items =
79 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
80 ASSERT_THAT(Items, ElementsAre(withName("callee")));
81 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
82 ASSERT_THAT(IncomingLevel1,
83 ElementsAre(AllOf(from(withName("caller1")),
84 fromRanges(Source.range("Callee")))));
85 auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
86 ASSERT_THAT(IncomingLevel2,
87 ElementsAre(AllOf(from(withName("caller2")),
88 fromRanges(Source.range("Caller1A"),
89 Source.range("Caller1B"))),
90 AllOf(from(withName("caller3")),
91 fromRanges(Source.range("Caller1C")))));
93 auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
94 ASSERT_THAT(IncomingLevel3,
95 ElementsAre(AllOf(from(withName("caller3")),
96 fromRanges(Source.range("Caller2")))));
98 auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
99 EXPECT_THAT(IncomingLevel4, IsEmpty());
102 TEST(CallHierarchy, IncomingOneFileObjC) {
103 Annotations Source(R"objc(
104 @implementation MyClass {}
105 +(void)call^ee {}
106 +(void) caller1 {
107 [MyClass $Callee[[callee]]];
109 +(void) caller2 {
110 [MyClass $Caller1A[[caller1]]];
111 [MyClass $Caller1B[[caller1]]];
113 +(void) caller3 {
114 [MyClass $Caller1C[[caller1]]];
115 [MyClass $Caller2[[caller2]]];
117 @end
118 )objc");
119 TestTU TU = TestTU::withCode(Source.code());
120 TU.Filename = "TestTU.m";
121 auto AST = TU.build();
122 auto Index = TU.index();
123 std::vector<CallHierarchyItem> Items =
124 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
125 ASSERT_THAT(Items, ElementsAre(withName("callee")));
126 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
127 ASSERT_THAT(IncomingLevel1,
128 ElementsAre(AllOf(from(withName("caller1")),
129 fromRanges(Source.range("Callee")))));
130 auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
131 ASSERT_THAT(IncomingLevel2,
132 ElementsAre(AllOf(from(withName("caller2")),
133 fromRanges(Source.range("Caller1A"),
134 Source.range("Caller1B"))),
135 AllOf(from(withName("caller3")),
136 fromRanges(Source.range("Caller1C")))));
138 auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
139 ASSERT_THAT(IncomingLevel3,
140 ElementsAre(AllOf(from(withName("caller3")),
141 fromRanges(Source.range("Caller2")))));
143 auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
144 EXPECT_THAT(IncomingLevel4, IsEmpty());
147 TEST(CallHierarchy, MainFileOnlyRef) {
148 // In addition to testing that we store refs to main-file only symbols,
149 // this tests that anonymous namespaces do not interfere with the
150 // symbol re-identification process in callHierarchyItemToSymbo().
151 Annotations Source(R"cpp(
152 void call^ee(int);
153 namespace {
154 void caller1() {
155 $Callee[[callee]](42);
158 void caller2() {
159 $Caller1[[caller1]]();
161 )cpp");
162 TestTU TU = TestTU::withCode(Source.code());
163 auto AST = TU.build();
164 auto Index = TU.index();
166 std::vector<CallHierarchyItem> Items =
167 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
168 ASSERT_THAT(Items, ElementsAre(withName("callee")));
169 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
170 ASSERT_THAT(IncomingLevel1,
171 ElementsAre(AllOf(from(withName("caller1")),
172 fromRanges(Source.range("Callee")))));
174 auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
175 EXPECT_THAT(IncomingLevel2,
176 ElementsAre(AllOf(from(withName("caller2")),
177 fromRanges(Source.range("Caller1")))));
180 TEST(CallHierarchy, IncomingQualified) {
181 Annotations Source(R"cpp(
182 namespace ns {
183 struct Waldo {
184 void find();
186 void Waldo::find() {}
187 void caller1(Waldo &W) {
188 W.$Caller1[[f^ind]]();
190 void caller2(Waldo &W) {
191 W.$Caller2[[find]]();
194 )cpp");
195 TestTU TU = TestTU::withCode(Source.code());
196 auto AST = TU.build();
197 auto Index = TU.index();
199 std::vector<CallHierarchyItem> Items =
200 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
201 ASSERT_THAT(Items, ElementsAre(withName("Waldo::find")));
202 auto Incoming = incomingCalls(Items[0], Index.get());
203 EXPECT_THAT(Incoming,
204 ElementsAre(AllOf(from(withName("caller1")),
205 fromRanges(Source.range("Caller1"))),
206 AllOf(from(withName("caller2")),
207 fromRanges(Source.range("Caller2")))));
210 TEST(CallHierarchy, IncomingMultiFileCpp) {
211 // The test uses a .hh suffix for header files to get clang
212 // to parse them in C++ mode. .h files are parsed in C mode
213 // by default, which causes problems because e.g. symbol
214 // USRs are different in C mode (do not include function signatures).
216 Annotations CalleeH(R"cpp(
217 void calle^e(int);
218 )cpp");
219 Annotations CalleeC(R"cpp(
220 #include "callee.hh"
221 void calle^e(int) {}
222 )cpp");
223 Annotations Caller1H(R"cpp(
224 void caller1();
225 )cpp");
226 Annotations Caller1C(R"cpp(
227 #include "callee.hh"
228 #include "caller1.hh"
229 void caller1() {
230 [[calle^e]](42);
232 )cpp");
233 Annotations Caller2H(R"cpp(
234 void caller2();
235 )cpp");
236 Annotations Caller2C(R"cpp(
237 #include "caller1.hh"
238 #include "caller2.hh"
239 void caller2() {
240 $A[[caller1]]();
241 $B[[caller1]]();
243 )cpp");
244 Annotations Caller3C(R"cpp(
245 #include "caller1.hh"
246 #include "caller2.hh"
247 void caller3() {
248 $Caller1[[caller1]]();
249 $Caller2[[caller2]]();
251 )cpp");
253 TestWorkspace Workspace;
254 Workspace.addSource("callee.hh", CalleeH.code());
255 Workspace.addSource("caller1.hh", Caller1H.code());
256 Workspace.addSource("caller2.hh", Caller2H.code());
257 Workspace.addMainFile("callee.cc", CalleeC.code());
258 Workspace.addMainFile("caller1.cc", Caller1C.code());
259 Workspace.addMainFile("caller2.cc", Caller2C.code());
260 Workspace.addMainFile("caller3.cc", Caller3C.code());
262 auto Index = Workspace.index();
264 auto CheckCallHierarchy = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
265 std::vector<CallHierarchyItem> Items =
266 prepareCallHierarchy(AST, Pos, TUPath);
267 ASSERT_THAT(Items, ElementsAre(withName("callee")));
268 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
269 ASSERT_THAT(IncomingLevel1,
270 ElementsAre(AllOf(from(withName("caller1")),
271 fromRanges(Caller1C.range()))));
273 auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
274 ASSERT_THAT(
275 IncomingLevel2,
276 ElementsAre(AllOf(from(withName("caller2")),
277 fromRanges(Caller2C.range("A"), Caller2C.range("B"))),
278 AllOf(from(withName("caller3")),
279 fromRanges(Caller3C.range("Caller1")))));
281 auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
282 ASSERT_THAT(IncomingLevel3,
283 ElementsAre(AllOf(from(withName("caller3")),
284 fromRanges(Caller3C.range("Caller2")))));
286 auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
287 EXPECT_THAT(IncomingLevel4, IsEmpty());
290 // Check that invoking from a call site works.
291 auto AST = Workspace.openFile("caller1.cc");
292 ASSERT_TRUE(bool(AST));
293 CheckCallHierarchy(*AST, Caller1C.point(), testPath("caller1.cc"));
295 // Check that invoking from the declaration site works.
296 AST = Workspace.openFile("callee.hh");
297 ASSERT_TRUE(bool(AST));
298 CheckCallHierarchy(*AST, CalleeH.point(), testPath("callee.hh"));
300 // Check that invoking from the definition site works.
301 AST = Workspace.openFile("callee.cc");
302 ASSERT_TRUE(bool(AST));
303 CheckCallHierarchy(*AST, CalleeC.point(), testPath("callee.cc"));
306 TEST(CallHierarchy, IncomingMultiFileObjC) {
307 // The test uses a .mi suffix for header files to get clang
308 // to parse them in ObjC mode. .h files are parsed in C mode
309 // by default, which causes problems because e.g. symbol
310 // USRs are different in C mode (do not include function signatures).
312 Annotations CalleeH(R"objc(
313 @interface CalleeClass
314 +(void)call^ee;
315 @end
316 )objc");
317 Annotations CalleeC(R"objc(
318 #import "callee.mi"
319 @implementation CalleeClass {}
320 +(void)call^ee {}
321 @end
322 )objc");
323 Annotations Caller1H(R"objc(
324 @interface Caller1Class
325 +(void)caller1;
326 @end
327 )objc");
328 Annotations Caller1C(R"objc(
329 #import "callee.mi"
330 #import "caller1.mi"
331 @implementation Caller1Class {}
332 +(void)caller1 {
333 [CalleeClass [[calle^e]]];
335 @end
336 )objc");
337 Annotations Caller2H(R"objc(
338 @interface Caller2Class
339 +(void)caller2;
340 @end
341 )objc");
342 Annotations Caller2C(R"objc(
343 #import "caller1.mi"
344 #import "caller2.mi"
345 @implementation Caller2Class {}
346 +(void)caller2 {
347 [Caller1Class $A[[caller1]]];
348 [Caller1Class $B[[caller1]]];
350 @end
351 )objc");
352 Annotations Caller3C(R"objc(
353 #import "caller1.mi"
354 #import "caller2.mi"
355 @implementation Caller3Class {}
356 +(void)caller3 {
357 [Caller1Class $Caller1[[caller1]]];
358 [Caller2Class $Caller2[[caller2]]];
360 @end
361 )objc");
363 TestWorkspace Workspace;
364 Workspace.addSource("callee.mi", CalleeH.code());
365 Workspace.addSource("caller1.mi", Caller1H.code());
366 Workspace.addSource("caller2.mi", Caller2H.code());
367 Workspace.addMainFile("callee.m", CalleeC.code());
368 Workspace.addMainFile("caller1.m", Caller1C.code());
369 Workspace.addMainFile("caller2.m", Caller2C.code());
370 Workspace.addMainFile("caller3.m", Caller3C.code());
371 auto Index = Workspace.index();
373 auto CheckCallHierarchy = [&](ParsedAST &AST, Position Pos, PathRef TUPath) {
374 std::vector<CallHierarchyItem> Items =
375 prepareCallHierarchy(AST, Pos, TUPath);
376 ASSERT_THAT(Items, ElementsAre(withName("callee")));
377 auto IncomingLevel1 = incomingCalls(Items[0], Index.get());
378 ASSERT_THAT(IncomingLevel1,
379 ElementsAre(AllOf(from(withName("caller1")),
380 fromRanges(Caller1C.range()))));
382 auto IncomingLevel2 = incomingCalls(IncomingLevel1[0].from, Index.get());
383 ASSERT_THAT(
384 IncomingLevel2,
385 ElementsAre(AllOf(from(withName("caller2")),
386 fromRanges(Caller2C.range("A"), Caller2C.range("B"))),
387 AllOf(from(withName("caller3")),
388 fromRanges(Caller3C.range("Caller1")))));
390 auto IncomingLevel3 = incomingCalls(IncomingLevel2[0].from, Index.get());
391 ASSERT_THAT(IncomingLevel3,
392 ElementsAre(AllOf(from(withName("caller3")),
393 fromRanges(Caller3C.range("Caller2")))));
395 auto IncomingLevel4 = incomingCalls(IncomingLevel3[0].from, Index.get());
396 EXPECT_THAT(IncomingLevel4, IsEmpty());
399 // Check that invoking from a call site works.
400 auto AST = Workspace.openFile("caller1.m");
401 ASSERT_TRUE(bool(AST));
402 CheckCallHierarchy(*AST, Caller1C.point(), testPath("caller1.m"));
404 // Check that invoking from the declaration site works.
405 AST = Workspace.openFile("callee.mi");
406 ASSERT_TRUE(bool(AST));
407 CheckCallHierarchy(*AST, CalleeH.point(), testPath("callee.mi"));
409 // Check that invoking from the definition site works.
410 AST = Workspace.openFile("callee.m");
411 ASSERT_TRUE(bool(AST));
412 CheckCallHierarchy(*AST, CalleeC.point(), testPath("callee.m"));
415 TEST(CallHierarchy, CallInLocalVarDecl) {
416 // Tests that local variable declarations are not treated as callers
417 // (they're not indexed, so they can't be represented as call hierarchy
418 // items); instead, the caller should be the containing function.
419 // However, namespace-scope variable declarations should be treated as
420 // callers because those are indexed and there is no enclosing entity
421 // that would be a useful caller.
422 Annotations Source(R"cpp(
423 int call^ee();
424 void caller1() {
425 $call1[[callee]]();
427 void caller2() {
428 int localVar = $call2[[callee]]();
430 int caller3 = $call3[[callee]]();
431 )cpp");
432 TestTU TU = TestTU::withCode(Source.code());
433 auto AST = TU.build();
434 auto Index = TU.index();
436 std::vector<CallHierarchyItem> Items =
437 prepareCallHierarchy(AST, Source.point(), testPath(TU.Filename));
438 ASSERT_THAT(Items, ElementsAre(withName("callee")));
440 auto Incoming = incomingCalls(Items[0], Index.get());
441 ASSERT_THAT(
442 Incoming,
443 ElementsAre(
444 AllOf(from(withName("caller1")), fromRanges(Source.range("call1"))),
445 AllOf(from(withName("caller2")), fromRanges(Source.range("call2"))),
446 AllOf(from(withName("caller3")), fromRanges(Source.range("call3")))));
449 } // namespace
450 } // namespace clangd
451 } // namespace clang