1 | //===-- SymbolInfoTests.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 "TestTU.h" |
11 | #include "XRefs.h" |
12 | #include "gmock/gmock.h" |
13 | #include "gtest/gtest.h" |
14 | #include <optional> |
15 | |
16 | namespace clang { |
17 | namespace clangd { |
18 | namespace { |
19 | |
20 | using ::testing::UnorderedElementsAreArray; |
21 | |
22 | // Partial SymbolDetails with the rest filled in at testing time. |
23 | struct ExpectedSymbolDetails { |
24 | std::string Name; |
25 | std::string Container; |
26 | std::string USR; |
27 | const char *DeclMarker = nullptr; |
28 | const char *DefMarker = nullptr; |
29 | }; |
30 | |
31 | TEST(SymbolInfoTests, All) { |
32 | std::pair<const char *, std::vector<ExpectedSymbolDetails>> |
33 | TestInputExpectedOutput[] = { |
34 | { |
35 | R"cpp( // Simple function reference - declaration |
36 | void $decl[[foo]](); |
37 | int bar() { |
38 | fo^o(); |
39 | return 0; |
40 | } |
41 | )cpp" , |
42 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@F@foo#" , .DeclMarker: "decl" }}}, |
43 | { |
44 | R"cpp( // Simple function reference - definition |
45 | void $def[[foo]]() {} |
46 | int bar() { |
47 | fo^o(); |
48 | return 0; |
49 | } |
50 | )cpp" , |
51 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@F@foo#" , .DeclMarker: "def" , .DefMarker: "def" }}}, |
52 | { |
53 | R"cpp( // Simple function reference - decl and def |
54 | void $decl[[foo]](); |
55 | void $def[[foo]]() {} |
56 | int bar() { |
57 | fo^o(); |
58 | return 0; |
59 | } |
60 | )cpp" , |
61 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@F@foo#" , .DeclMarker: "decl" , .DefMarker: "def" }}}, |
62 | { |
63 | R"cpp( // Simple class reference - decl and def |
64 | @interface $decl[[Foo]] |
65 | @end |
66 | @implementation $def[[Foo]] |
67 | @end |
68 | void doSomething(F^oo *obj) {} |
69 | )cpp" , |
70 | {ExpectedSymbolDetails{.Name: "Foo" , .Container: "" , .USR: "c:objc(cs)Foo" , .DeclMarker: "decl" , |
71 | .DefMarker: "def" }}}, |
72 | { |
73 | R"cpp( // Simple method reference - decl and def |
74 | @interface Foo |
75 | - (void)$decl[[foo]]; |
76 | @end |
77 | @implementation Foo |
78 | - (void)$def[[fo^o]] {} |
79 | @end |
80 | )cpp" , |
81 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "Foo::" , .USR: "c:objc(cs)Foo(im)foo" , |
82 | .DeclMarker: "decl" , .DefMarker: "def" }}}, |
83 | { |
84 | R"cpp( // Function in namespace reference |
85 | namespace bar { |
86 | void $decl[[foo]](); |
87 | int baz() { |
88 | fo^o(); |
89 | return 0; |
90 | } |
91 | } |
92 | )cpp" , |
93 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "bar::" , .USR: "c:@N@bar@F@foo#" , |
94 | .DeclMarker: "decl" }}}, |
95 | { |
96 | R"cpp( // Function in different namespace reference |
97 | namespace bar { |
98 | void $decl[[foo]](); |
99 | } |
100 | namespace barbar { |
101 | int baz() { |
102 | bar::fo^o(); |
103 | return 0; |
104 | } |
105 | } |
106 | )cpp" , |
107 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "bar::" , .USR: "c:@N@bar@F@foo#" , |
108 | .DeclMarker: "decl" }}}, |
109 | { |
110 | R"cpp( // Function in global namespace reference |
111 | void $decl[[foo]](); |
112 | namespace Nbar { |
113 | namespace Nbaz { |
114 | int baz() { |
115 | ::fo^o(); |
116 | return 0; |
117 | } |
118 | } |
119 | } |
120 | )cpp" , |
121 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@F@foo#" , .DeclMarker: "decl" }}}, |
122 | { |
123 | R"cpp( // Function in anonymous namespace reference |
124 | namespace { |
125 | void $decl[[foo]](); |
126 | } |
127 | namespace barbar { |
128 | int baz() { |
129 | fo^o(); |
130 | return 0; |
131 | } |
132 | } |
133 | )cpp" , |
134 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "(anonymous)" , |
135 | .USR: "c:TestTU.cpp@aN@F@foo#" , .DeclMarker: "decl" }}}, |
136 | { |
137 | R"cpp( // Function reference - ADL |
138 | namespace bar { |
139 | struct BarType {}; |
140 | void $decl[[foo]](const BarType&); |
141 | } |
142 | namespace barbar { |
143 | int baz() { |
144 | bar::BarType b; |
145 | fo^o(b); |
146 | return 0; |
147 | } |
148 | } |
149 | )cpp" , |
150 | {ExpectedSymbolDetails{ |
151 | .Name: "foo" , .Container: "bar::" , .USR: "c:@N@bar@F@foo#&1$@N@bar@S@BarType#" , |
152 | .DeclMarker: "decl" }}}, |
153 | { |
154 | R"cpp( // Global value reference |
155 | int $def[[value]]; |
156 | void foo(int) { } |
157 | void bar() { |
158 | foo(val^ue); |
159 | } |
160 | )cpp" , |
161 | {ExpectedSymbolDetails{.Name: "value" , .Container: "" , .USR: "c:@value" , .DeclMarker: "def" , .DefMarker: "def" }}}, |
162 | { |
163 | R"cpp( // Local value reference |
164 | void foo() { int $def[[aaa]]; int bbb = aa^a; } |
165 | )cpp" , |
166 | {ExpectedSymbolDetails{.Name: "aaa" , .Container: "foo" , .USR: "c:TestTU.cpp@49@F@foo#@aaa" , |
167 | .DeclMarker: "def" , .DefMarker: "def" }}}, |
168 | { |
169 | R"cpp( // Function param |
170 | void bar(int $def[[aaa]]) { |
171 | int bbb = a^aa; |
172 | } |
173 | )cpp" , |
174 | {ExpectedSymbolDetails{ |
175 | .Name: "aaa" , .Container: "bar" , .USR: "c:TestTU.cpp@38@F@bar#I#@aaa" , .DeclMarker: "def" , .DefMarker: "def" }}}, |
176 | { |
177 | R"cpp( // Lambda capture |
178 | void foo() { |
179 | int $def[[ii]]; |
180 | auto lam = [ii]() { |
181 | return i^i; |
182 | }; |
183 | } |
184 | )cpp" , |
185 | {ExpectedSymbolDetails{.Name: "ii" , .Container: "foo" , .USR: "c:TestTU.cpp@54@F@foo#@ii" , |
186 | .DeclMarker: "def" , .DefMarker: "def" }}}, |
187 | { |
188 | R"cpp( // Macro reference |
189 | #define MACRO 5\nint i = MAC^RO; |
190 | )cpp" , |
191 | {ExpectedSymbolDetails{.Name: "MACRO" , .Container: "" , |
192 | .USR: "c:TestTU.cpp@38@macro@MACRO" }}}, |
193 | { |
194 | R"cpp( // Macro reference |
195 | #define MACRO 5\nint i = MACRO^; |
196 | )cpp" , |
197 | {ExpectedSymbolDetails{.Name: "MACRO" , .Container: "" , |
198 | .USR: "c:TestTU.cpp@38@macro@MACRO" }}}, |
199 | { |
200 | R"cpp( // Multiple symbols returned - using overloaded function name |
201 | void $def[[foo]]() {} |
202 | void $def_bool[[foo]](bool) {} |
203 | void $def_int[[foo]](int) {} |
204 | namespace bar { |
205 | using ::$decl[[fo^o]]; |
206 | } |
207 | )cpp" , |
208 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@F@foo#" , .DeclMarker: "def" , .DefMarker: "def" }, |
209 | ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@F@foo#b#" , .DeclMarker: "def_bool" , |
210 | .DefMarker: "def_bool" }, |
211 | ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@F@foo#I#" , .DeclMarker: "def_int" , |
212 | .DefMarker: "def_int" }, |
213 | ExpectedSymbolDetails{.Name: "foo" , .Container: "bar::" , .USR: "c:@N@bar@UD@foo" , |
214 | .DeclMarker: "decl" }}}, |
215 | { |
216 | R"cpp( // Multiple symbols returned - implicit conversion |
217 | struct foo {}; |
218 | struct bar { |
219 | bar(const foo&) {} |
220 | }; |
221 | void func_baz1(bar) {} |
222 | void func_baz2() { |
223 | foo $def[[ff]]; |
224 | func_baz1(f^f); |
225 | } |
226 | )cpp" , |
227 | {ExpectedSymbolDetails{.Name: "ff" , .Container: "func_baz2" , |
228 | .USR: "c:TestTU.cpp@218@F@func_baz2#@ff" , .DeclMarker: "def" , |
229 | .DefMarker: "def" }}}, |
230 | { |
231 | R"cpp( // Type reference - declaration |
232 | struct $decl[[foo]]; |
233 | void bar(fo^o*); |
234 | )cpp" , |
235 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@S@foo" , .DeclMarker: "decl" }}}, |
236 | { |
237 | R"cpp( // Type reference - definition |
238 | struct $def[[foo]] {}; |
239 | void bar(fo^o*); |
240 | )cpp" , |
241 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@S@foo" , .DeclMarker: "def" , .DefMarker: "def" }}}, |
242 | { |
243 | R"cpp( // Type Reference - template argument |
244 | struct $def[[foo]] {}; |
245 | template<class T> struct bar {}; |
246 | void baz() { |
247 | bar<fo^o> b; |
248 | } |
249 | )cpp" , |
250 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@S@foo" , .DeclMarker: "def" , .DefMarker: "def" }}}, |
251 | { |
252 | R"cpp( // Template parameter reference - type param |
253 | template<class $def[[TT]]> struct bar { |
254 | T^T t; |
255 | }; |
256 | )cpp" , |
257 | {ExpectedSymbolDetails{.Name: "TT" , .Container: "bar::" , .USR: "c:TestTU.cpp@65" , .DeclMarker: "def" , |
258 | .DefMarker: "def" }}}, |
259 | { |
260 | R"cpp( // Template parameter reference - type param |
261 | template<int $def[[NN]]> struct bar { |
262 | int a = N^N; |
263 | }; |
264 | )cpp" , |
265 | {ExpectedSymbolDetails{.Name: "NN" , .Container: "bar::" , .USR: "c:TestTU.cpp@65" , .DeclMarker: "def" , |
266 | .DefMarker: "def" }}}, |
267 | { |
268 | R"cpp( // Class member reference - objec |
269 | struct foo { |
270 | int $def[[aa]]; |
271 | }; |
272 | void bar() { |
273 | foo f; |
274 | f.a^a; |
275 | } |
276 | )cpp" , |
277 | {ExpectedSymbolDetails{.Name: "aa" , .Container: "foo::" , .USR: "c:@S@foo@FI@aa" , .DeclMarker: "def" , |
278 | .DefMarker: "def" }}}, |
279 | { |
280 | R"cpp( // Class member reference - pointer |
281 | struct foo { |
282 | int $def[[aa]]; |
283 | }; |
284 | void bar() { |
285 | &foo::a^a; |
286 | } |
287 | )cpp" , |
288 | {ExpectedSymbolDetails{.Name: "aa" , .Container: "foo::" , .USR: "c:@S@foo@FI@aa" , .DeclMarker: "def" , |
289 | .DefMarker: "def" }}}, |
290 | { |
291 | R"cpp( // Class method reference - objec |
292 | struct foo { |
293 | void $def[[aa]]() {} |
294 | }; |
295 | void bar() { |
296 | foo f; |
297 | f.a^a(); |
298 | } |
299 | )cpp" , |
300 | {ExpectedSymbolDetails{.Name: "aa" , .Container: "foo::" , .USR: "c:@S@foo@F@aa#" , .DeclMarker: "def" , |
301 | .DefMarker: "def" }}}, |
302 | { |
303 | R"cpp( // Class method reference - pointer |
304 | struct foo { |
305 | void $def[[aa]]() {} |
306 | }; |
307 | void bar() { |
308 | &foo::a^a; |
309 | } |
310 | )cpp" , |
311 | {ExpectedSymbolDetails{.Name: "aa" , .Container: "foo::" , .USR: "c:@S@foo@F@aa#" , .DeclMarker: "def" , |
312 | .DefMarker: "def" }}}, |
313 | { |
314 | R"cpp( // Typedef |
315 | typedef int $decl[[foo]]; |
316 | void bar() { |
317 | fo^o a; |
318 | } |
319 | )cpp" , |
320 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:TestTU.cpp@T@foo" , .DeclMarker: "decl" }}}, |
321 | { |
322 | R"cpp( // Type alias |
323 | using $decl[[foo]] = int; |
324 | void bar() { |
325 | fo^o a; |
326 | } |
327 | )cpp" , |
328 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@foo" , .DeclMarker: "decl" }}}, |
329 | { |
330 | R"cpp( // Namespace reference |
331 | namespace $decl[[foo]] {} |
332 | using namespace fo^o; |
333 | )cpp" , |
334 | {ExpectedSymbolDetails{.Name: "foo" , .Container: "" , .USR: "c:@N@foo" , .DeclMarker: "decl" }}}, |
335 | { |
336 | R"cpp( // Enum value reference |
337 | enum foo { $def[[bar]], baz }; |
338 | void f() { |
339 | foo fff = ba^r; |
340 | } |
341 | )cpp" , |
342 | {ExpectedSymbolDetails{.Name: "bar" , .Container: "foo" , .USR: "c:@E@foo@bar" , .DeclMarker: "def" , |
343 | .DefMarker: "def" }}}, |
344 | { |
345 | R"cpp( // Enum class value reference |
346 | enum class foo { $def[[bar]], baz }; |
347 | void f() { |
348 | foo fff = foo::ba^r; |
349 | } |
350 | )cpp" , |
351 | {ExpectedSymbolDetails{.Name: "bar" , .Container: "foo::" , .USR: "c:@E@foo@bar" , .DeclMarker: "def" , |
352 | .DefMarker: "def" }}}, |
353 | { |
354 | R"cpp( // Parameters in declarations |
355 | void foo(int $def[[ba^r]]); |
356 | )cpp" , |
357 | {ExpectedSymbolDetails{ |
358 | .Name: "bar" , .Container: "foo" , .USR: "c:TestTU.cpp@50@F@foo#I#@bar" , .DeclMarker: "def" , .DefMarker: "def" }}}, |
359 | { |
360 | R"cpp( // Type inference with auto keyword |
361 | struct foo {}; |
362 | foo getfoo() { return foo{}; } |
363 | void f() { |
364 | au^to a = getfoo(); |
365 | } |
366 | )cpp" , |
367 | {/* not implemented */}}, |
368 | { |
369 | R"cpp( // decltype |
370 | struct foo {}; |
371 | void f() { |
372 | foo f; |
373 | declt^ype(f); |
374 | } |
375 | )cpp" , |
376 | {/* not implemented */}}, |
377 | }; |
378 | |
379 | for (const auto &T : TestInputExpectedOutput) { |
380 | Annotations TestInput(T.first); |
381 | TestTU TU; |
382 | TU.Code = std::string(TestInput.code()); |
383 | TU.ExtraArgs.push_back(x: "-xobjective-c++" ); |
384 | auto AST = TU.build(); |
385 | |
386 | std::vector<SymbolDetails> Expected; |
387 | for (const auto &Sym : T.second) { |
388 | std::optional<Location> Decl, Def; |
389 | if (Sym.DeclMarker) |
390 | Decl = Location{.uri: URIForFile::canonicalize(AbsPath: testPath(File: TU.Filename), TUPath: "" ), |
391 | .range: TestInput.range(Name: Sym.DeclMarker)}; |
392 | if (Sym.DefMarker) |
393 | Def = Location{.uri: URIForFile::canonicalize(AbsPath: testPath(File: TU.Filename), TUPath: "" ), |
394 | .range: TestInput.range(Name: Sym.DefMarker)}; |
395 | Expected.push_back( |
396 | x: {.name: Sym.Name, .containerName: Sym.Container, .USR: Sym.USR, .ID: SymbolID(Sym.USR), .declarationRange: Decl, .definitionRange: Def}); |
397 | } |
398 | |
399 | EXPECT_THAT(getSymbolInfo(AST, TestInput.point()), |
400 | UnorderedElementsAreArray(Expected)) |
401 | << T.first; |
402 | } |
403 | } |
404 | |
405 | } // namespace |
406 | } // namespace clangd |
407 | } // namespace clang |
408 | |