1 | //===-- HeuristicResolverTests.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 "clang/Sema/HeuristicResolver.h" |
9 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
10 | #include "clang/ASTMatchers/ASTMatchers.h" |
11 | #include "clang/Tooling/Tooling.h" |
12 | #include "gmock/gmock-matchers.h" |
13 | #include "gtest/gtest.h" |
14 | |
15 | using namespace clang::ast_matchers; |
16 | using testing::ElementsAre; |
17 | |
18 | namespace clang { |
19 | namespace { |
20 | |
21 | // Helper for matching a sequence of elements with a variadic list of matchers. |
22 | // Usage: `ElementsAre(matchAdapter(Vs, MatchFunction)...)`, where `Vs...` is |
23 | // a variadic list of matchers. |
24 | // For each `V` in `Vs`, this will match the corresponding element `E` if |
25 | // `MatchFunction(V, E)` is true. |
26 | MATCHER_P2(matchAdapter, MatcherForElement, MatchFunction, "matchAdapter" ) { |
27 | return MatchFunction(MatcherForElement, arg); |
28 | } |
29 | |
30 | template <typename InputNode> |
31 | using ResolveFnT = std::function<std::vector<const NamedDecl *>( |
32 | const HeuristicResolver *, const InputNode *)>; |
33 | |
34 | // Test heuristic resolution on `Code` using the resolution procedure |
35 | // `ResolveFn`, which takes a `HeuristicResolver` and an input AST node of type |
36 | // `InputNode` and returns a `std::vector<const NamedDecl *>`. |
37 | // `InputMatcher` should be an AST matcher that matches a single node to pass as |
38 | // input to `ResolveFn`, bound to the ID "input". `OutputMatchers` should be AST |
39 | // matchers that each match a single node, bound to the ID "output". |
40 | template <typename InputNode, typename InputMatcher, typename... OutputMatchers> |
41 | void expectResolution(llvm::StringRef Code, ResolveFnT<InputNode> ResolveFn, |
42 | const InputMatcher &IM, const OutputMatchers &...OMS) { |
43 | auto TU = tooling::buildASTFromCodeWithArgs(Code, Args: {"-std=c++20" }); |
44 | auto &Ctx = TU->getASTContext(); |
45 | auto InputMatches = match(IM, Ctx); |
46 | ASSERT_EQ(1u, InputMatches.size()); |
47 | const auto *Input = InputMatches[0].template getNodeAs<InputNode>("input" ); |
48 | ASSERT_TRUE(Input); |
49 | |
50 | auto OutputNodeMatches = [&](auto &OutputMatcher, auto &Actual) { |
51 | auto OutputMatches = match(OutputMatcher, Ctx); |
52 | if (OutputMatches.size() != 1u) |
53 | return false; |
54 | const auto *ExpectedOutput = |
55 | OutputMatches[0].template getNodeAs<NamedDecl>("output" ); |
56 | if (!ExpectedOutput) |
57 | return false; |
58 | return ExpectedOutput == Actual; |
59 | }; |
60 | |
61 | HeuristicResolver H(Ctx); |
62 | auto Results = ResolveFn(&H, Input); |
63 | EXPECT_THAT(Results, ElementsAre(matchAdapter(OMS, OutputNodeMatches)...)); |
64 | } |
65 | |
66 | // Wrapper for the above that accepts a HeuristicResolver member function |
67 | // pointer directly. |
68 | template <typename InputNode, typename InputMatcher, typename... OutputMatchers> |
69 | void expectResolution(llvm::StringRef Code, |
70 | std::vector<const NamedDecl *> ( |
71 | HeuristicResolver::*ResolveFn)(const InputNode *) |
72 | const, |
73 | const InputMatcher &IM, const OutputMatchers &...OMS) { |
74 | expectResolution(Code, ResolveFnT<InputNode>(std::mem_fn(ResolveFn)), IM, |
75 | OMS...); |
76 | } |
77 | |
78 | TEST(HeuristicResolver, MemberExpr) { |
79 | std::string Code = R"cpp( |
80 | template <typename T> |
81 | struct S { |
82 | void bar() {} |
83 | }; |
84 | |
85 | template <typename T> |
86 | void foo(S<T> arg) { |
87 | arg.bar(); |
88 | } |
89 | )cpp" ; |
90 | // Test resolution of "bar" in "arg.bar()". |
91 | expectResolution( |
92 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
93 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "bar" )).bind(ID: "input" ), |
94 | OMS: cxxMethodDecl(hasName(Name: "bar" )).bind(ID: "output" )); |
95 | } |
96 | |
97 | TEST(HeuristicResolver, MemberExpr_Overloads) { |
98 | std::string Code = R"cpp( |
99 | template <typename T> |
100 | struct S { |
101 | void bar(int); |
102 | void bar(float); |
103 | }; |
104 | |
105 | template <typename T, typename U> |
106 | void foo(S<T> arg, U u) { |
107 | arg.bar(u); |
108 | } |
109 | )cpp" ; |
110 | // Test resolution of "bar" in "arg.bar(u)". Both overloads should be found. |
111 | expectResolution( |
112 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
113 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "bar" )).bind(ID: "input" ), |
114 | OMS: cxxMethodDecl(hasName(Name: "bar" ), hasParameter(N: 0, InnerMatcher: hasType(InnerMatcher: asString(Name: "int" )))) |
115 | .bind(ID: "output" ), |
116 | OMS: cxxMethodDecl(hasName(Name: "bar" ), hasParameter(N: 0, InnerMatcher: hasType(InnerMatcher: asString(Name: "float" )))) |
117 | .bind(ID: "output" )); |
118 | } |
119 | |
120 | TEST(HeuristicResolver, MemberExpr_SmartPointer) { |
121 | std::string Code = R"cpp( |
122 | template <typename> struct S { void foo() {} }; |
123 | template <typename T> struct unique_ptr { |
124 | T* operator->(); |
125 | }; |
126 | template <typename T> |
127 | void test(unique_ptr<S<T>>& v) { |
128 | v->foo(); |
129 | } |
130 | )cpp" ; |
131 | // Test resolution of "foo" in "v->foo()". |
132 | expectResolution( |
133 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
134 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "foo" )).bind(ID: "input" ), |
135 | OMS: cxxMethodDecl(hasName(Name: "foo" )).bind(ID: "output" )); |
136 | } |
137 | |
138 | TEST(HeuristicResolver, MemberExpr_SmartPointer_Qualified) { |
139 | std::string Code = R"cpp( |
140 | template <typename> struct Waldo { |
141 | void find(); |
142 | void find() const; |
143 | }; |
144 | template <typename T> struct unique_ptr { |
145 | T* operator->(); |
146 | }; |
147 | template <typename T> |
148 | void test(unique_ptr<const Waldo<T>>& w) { |
149 | w->find(); |
150 | } |
151 | )cpp" ; |
152 | expectResolution( |
153 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
154 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "find" )).bind(ID: "input" ), |
155 | OMS: cxxMethodDecl(hasName(Name: "find" ), isConst()).bind(ID: "output" )); |
156 | } |
157 | |
158 | TEST(HeuristicResolver, MemberExpr_Static_Qualified) { |
159 | std::string Code = R"cpp( |
160 | template <typename T> |
161 | struct Waldo { |
162 | static void find(); |
163 | }; |
164 | template <typename T> |
165 | void foo(const Waldo<T>& t) { |
166 | t.find(); |
167 | } |
168 | )cpp" ; |
169 | // Test resolution of "find" in "t.find()". |
170 | // The object being `const` should have no bearing on a call to a static |
171 | // method. |
172 | expectResolution( |
173 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
174 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "find" )).bind(ID: "input" ), |
175 | OMS: cxxMethodDecl(hasName(Name: "find" )).bind(ID: "output" )); |
176 | } |
177 | |
178 | TEST(HeuristicResolver, MemberExpr_AutoTypeDeduction1) { |
179 | std::string Code = R"cpp( |
180 | template <typename T> |
181 | struct A { |
182 | int waldo; |
183 | }; |
184 | template <typename T> |
185 | void foo(A<T> a) { |
186 | auto copy = a; |
187 | copy.waldo; |
188 | } |
189 | )cpp" ; |
190 | expectResolution( |
191 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
192 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "waldo" )).bind(ID: "input" ), |
193 | OMS: fieldDecl(hasName(Name: "waldo" )).bind(ID: "output" )); |
194 | } |
195 | |
196 | TEST(HeuristicResolver, MemberExpr_AutoTypeDeduction2) { |
197 | std::string Code = R"cpp( |
198 | struct B { |
199 | int waldo; |
200 | }; |
201 | |
202 | template <typename T> |
203 | struct A { |
204 | B b; |
205 | }; |
206 | template <typename T> |
207 | void foo(A<T> a) { |
208 | auto b = a.b; |
209 | b.waldo; |
210 | } |
211 | )cpp" ; |
212 | expectResolution( |
213 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
214 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "waldo" )).bind(ID: "input" ), |
215 | OMS: fieldDecl(hasName(Name: "waldo" )).bind(ID: "output" )); |
216 | } |
217 | |
218 | TEST(HeuristicResolver, MemberExpr_Chained) { |
219 | std::string Code = R"cpp( |
220 | struct A { void foo() {} }; |
221 | template <typename T> |
222 | struct B { |
223 | A func(int); |
224 | void bar() { |
225 | func(1).foo(); |
226 | } |
227 | }; |
228 | )cpp" ; |
229 | // Test resolution of "foo" in "func(1).foo()". |
230 | expectResolution( |
231 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
232 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "foo" )).bind(ID: "input" ), |
233 | OMS: cxxMethodDecl(hasName(Name: "foo" )).bind(ID: "output" )); |
234 | } |
235 | |
236 | TEST(HeuristicResolver, MemberExpr_ReferenceType) { |
237 | std::string Code = R"cpp( |
238 | struct B { |
239 | int waldo; |
240 | }; |
241 | template <typename T> |
242 | struct A { |
243 | B &b; |
244 | }; |
245 | template <typename T> |
246 | void foo(A<T> &a) { |
247 | a.b.waldo; |
248 | } |
249 | )cpp" ; |
250 | // Test resolution of "waldo" in "a.b.waldo". |
251 | expectResolution( |
252 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
253 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "waldo" )).bind(ID: "input" ), |
254 | OMS: fieldDecl(hasName(Name: "waldo" )).bind(ID: "output" )); |
255 | } |
256 | |
257 | TEST(HeuristicResolver, MemberExpr_PointerType) { |
258 | std::string Code = R"cpp( |
259 | struct B { |
260 | int waldo; |
261 | }; |
262 | template <typename T> |
263 | struct A { |
264 | B *b; |
265 | }; |
266 | template <typename T> |
267 | void foo(A<T> &a) { |
268 | a.b->waldo; |
269 | } |
270 | )cpp" ; |
271 | // Test resolution of "waldo" in "a.b->waldo". |
272 | expectResolution( |
273 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
274 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "waldo" )).bind(ID: "input" ), |
275 | OMS: fieldDecl(hasName(Name: "waldo" )).bind(ID: "output" )); |
276 | } |
277 | |
278 | TEST(HeuristicResolver, MemberExpr_TemplateArgs) { |
279 | std::string Code = R"cpp( |
280 | struct Foo { |
281 | static Foo k(int); |
282 | template <typename T> T convert(); |
283 | }; |
284 | template <typename T> |
285 | void test() { |
286 | Foo::k(T()).template convert<T>(); |
287 | } |
288 | )cpp" ; |
289 | // Test resolution of "convert" in "Foo::k(T()).template convert<T>()". |
290 | expectResolution( |
291 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
292 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "convert" )).bind(ID: "input" ), |
293 | OMS: functionTemplateDecl(hasName(Name: "convert" )).bind(ID: "output" )); |
294 | } |
295 | |
296 | TEST(HeuristicResolver, MemberExpr_TypeAlias) { |
297 | std::string Code = R"cpp( |
298 | template <typename T> |
299 | struct Waldo { |
300 | void find(); |
301 | }; |
302 | template <typename T> |
303 | using Wally = Waldo<T>; |
304 | template <typename T> |
305 | void foo(Wally<T> w) { |
306 | w.find(); |
307 | } |
308 | )cpp" ; |
309 | // Test resolution of "find" in "w.find()". |
310 | expectResolution( |
311 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
312 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "find" )).bind(ID: "input" ), |
313 | OMS: cxxMethodDecl(hasName(Name: "find" )).bind(ID: "output" )); |
314 | } |
315 | |
316 | TEST(HeuristicResolver, MemberExpr_BaseClass_TypeAlias) { |
317 | std::string Code = R"cpp( |
318 | template <typename T> |
319 | struct Waldo { |
320 | void find(); |
321 | }; |
322 | template <typename T> |
323 | using Wally = Waldo<T>; |
324 | template <typename T> |
325 | struct S : Wally<T> { |
326 | void foo() { |
327 | this->find(); |
328 | } |
329 | }; |
330 | )cpp" ; |
331 | // Test resolution of "find" in "this->find()". |
332 | expectResolution( |
333 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
334 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "find" )).bind(ID: "input" ), |
335 | OMS: cxxMethodDecl(hasName(Name: "find" )).bind(ID: "output" )); |
336 | } |
337 | |
338 | TEST(HeuristicResolver, MemberExpr_Metafunction) { |
339 | std::string Code = R"cpp( |
340 | template <typename T> |
341 | struct Waldo { |
342 | void find(); |
343 | }; |
344 | template <typename T> |
345 | struct MetaWaldo { |
346 | using Type = Waldo<T>; |
347 | }; |
348 | template <typename T> |
349 | void foo(typename MetaWaldo<T>::Type w) { |
350 | w.find(); |
351 | } |
352 | )cpp" ; |
353 | // Test resolution of "find" in "w.find()". |
354 | expectResolution( |
355 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
356 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "find" )).bind(ID: "input" ), |
357 | OMS: cxxMethodDecl(hasName(Name: "find" )).bind(ID: "output" )); |
358 | } |
359 | |
360 | TEST(HeuristicResolver, MemberExpr_Metafunction_Enumerator) { |
361 | std::string Code = R"cpp( |
362 | enum class State { Hidden }; |
363 | template <typename T> |
364 | struct Meta { |
365 | using Type = State; |
366 | }; |
367 | template <typename T> |
368 | void foo(typename Meta<T>::Type t) { |
369 | t.Hidden; |
370 | } |
371 | )cpp" ; |
372 | // Test resolution of "Hidden" in "t.Hidden". |
373 | expectResolution( |
374 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
375 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "Hidden" )).bind(ID: "input" ), |
376 | OMS: enumConstantDecl(hasName(Name: "Hidden" )).bind(ID: "output" )); |
377 | } |
378 | |
379 | TEST(HeuristicResolver, MemberExpr_DeducedNonTypeTemplateParameter) { |
380 | std::string Code = R"cpp( |
381 | template <int N> |
382 | struct Waldo { |
383 | const int found = N; |
384 | }; |
385 | template <Waldo W> |
386 | int foo() { |
387 | return W.found; |
388 | } |
389 | )cpp" ; |
390 | // Test resolution of "found" in "W.found". |
391 | expectResolution( |
392 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
393 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "found" )).bind(ID: "input" ), |
394 | OMS: fieldDecl(hasName(Name: "found" )).bind(ID: "output" )); |
395 | } |
396 | |
397 | TEST(HeuristicResolver, MemberExpr_HangIssue126536) { |
398 | std::string Code = R"cpp( |
399 | template <class T> |
400 | void foo() { |
401 | T bar; |
402 | auto baz = (bar, bar); |
403 | baz.foo(); |
404 | } |
405 | )cpp" ; |
406 | // Test resolution of "foo" in "baz.foo()". |
407 | // Here, we are testing that we do not get into an infinite loop. |
408 | expectResolution( |
409 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
410 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "foo" )).bind(ID: "input" )); |
411 | } |
412 | |
413 | TEST(HeuristicResolver, MemberExpr_DefaultTemplateArgument) { |
414 | std::string Code = R"cpp( |
415 | struct Default { |
416 | void foo(); |
417 | }; |
418 | template <typename T = Default> |
419 | void bar(T t) { |
420 | t.foo(); |
421 | } |
422 | )cpp" ; |
423 | // Test resolution of "foo" in "t.foo()". |
424 | expectResolution( |
425 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
426 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "foo" )).bind(ID: "input" ), |
427 | OMS: cxxMethodDecl(hasName(Name: "foo" )).bind(ID: "output" )); |
428 | } |
429 | |
430 | TEST(HeuristicResolver, MemberExpr_DefaultTemplateArgument_Recursive) { |
431 | std::string Code = R"cpp( |
432 | struct Default { |
433 | void foo(); |
434 | }; |
435 | template <typename D = Default, typename T = D> |
436 | void bar(T t) { |
437 | t.foo(); |
438 | } |
439 | )cpp" ; |
440 | // Test resolution of "foo" in "t.foo()". |
441 | expectResolution( |
442 | Code, ResolveFn: &HeuristicResolver::resolveMemberExpr, |
443 | IM: cxxDependentScopeMemberExpr(hasMemberName(N: "foo" )).bind(ID: "input" ), |
444 | OMS: cxxMethodDecl(hasName(Name: "foo" )).bind(ID: "output" )); |
445 | } |
446 | |
447 | TEST(HeuristicResolver, DeclRefExpr_StaticMethod) { |
448 | std::string Code = R"cpp( |
449 | template <typename T> |
450 | struct S { |
451 | static void bar() {} |
452 | }; |
453 | |
454 | template <typename T> |
455 | void foo() { |
456 | S<T>::bar(); |
457 | } |
458 | )cpp" ; |
459 | // Test resolution of "bar" in "S<T>::bar()". |
460 | expectResolution( |
461 | Code, ResolveFn: &HeuristicResolver::resolveDeclRefExpr, |
462 | IM: dependentScopeDeclRefExpr(hasDependentName(N: "bar" )).bind(ID: "input" ), |
463 | OMS: cxxMethodDecl(hasName(Name: "bar" )).bind(ID: "output" )); |
464 | } |
465 | |
466 | TEST(HeuristicResolver, DeclRefExpr_DefaultTemplateArgument) { |
467 | std::string Code = R"cpp( |
468 | struct Default { |
469 | static void foo(); |
470 | }; |
471 | template <typename T = Default> |
472 | void bar() { |
473 | T::foo(); |
474 | } |
475 | )cpp" ; |
476 | // Test resolution of "foo" in "T::foo()". |
477 | expectResolution( |
478 | Code, ResolveFn: &HeuristicResolver::resolveDeclRefExpr, |
479 | IM: dependentScopeDeclRefExpr(hasDependentName(N: "foo" )).bind(ID: "input" ), |
480 | OMS: cxxMethodDecl(hasName(Name: "foo" )).bind(ID: "output" )); |
481 | } |
482 | |
483 | TEST(HeuristicResolver, DeclRefExpr_StaticOverloads) { |
484 | std::string Code = R"cpp( |
485 | template <typename T> |
486 | struct S { |
487 | static void bar(int); |
488 | static void bar(float); |
489 | }; |
490 | |
491 | template <typename T, typename U> |
492 | void foo(U u) { |
493 | S<T>::bar(u); |
494 | } |
495 | )cpp" ; |
496 | // Test resolution of "bar" in "S<T>::bar(u)". Both overloads should be found. |
497 | expectResolution( |
498 | Code, ResolveFn: &HeuristicResolver::resolveDeclRefExpr, |
499 | IM: dependentScopeDeclRefExpr(hasDependentName(N: "bar" )).bind(ID: "input" ), |
500 | OMS: cxxMethodDecl(hasName(Name: "bar" ), hasParameter(N: 0, InnerMatcher: hasType(InnerMatcher: asString(Name: "int" )))) |
501 | .bind(ID: "output" ), |
502 | OMS: cxxMethodDecl(hasName(Name: "bar" ), hasParameter(N: 0, InnerMatcher: hasType(InnerMatcher: asString(Name: "float" )))) |
503 | .bind(ID: "output" )); |
504 | } |
505 | |
506 | TEST(HeuristicResolver, DeclRefExpr_Enumerator) { |
507 | std::string Code = R"cpp( |
508 | template <typename T> |
509 | struct Foo { |
510 | enum class E { A, B }; |
511 | E e = E::A; |
512 | }; |
513 | )cpp" ; |
514 | // Test resolution of "A" in "E::A". |
515 | expectResolution( |
516 | Code, ResolveFn: &HeuristicResolver::resolveDeclRefExpr, |
517 | IM: dependentScopeDeclRefExpr(hasDependentName(N: "A" )).bind(ID: "input" ), |
518 | OMS: enumConstantDecl(hasName(Name: "A" )).bind(ID: "output" )); |
519 | } |
520 | |
521 | TEST(HeuristicResolver, DeclRefExpr_RespectScope) { |
522 | std::string Code = R"cpp( |
523 | template <typename Info> |
524 | struct PointerIntPair { |
525 | void *getPointer() const { return Info::getPointer(); } |
526 | }; |
527 | )cpp" ; |
528 | // Test resolution of "getPointer" in "Info::getPointer()". |
529 | // Here, we are testing that we do not incorrectly get the enclosing |
530 | // getPointer() function as a result. |
531 | expectResolution( |
532 | Code, ResolveFn: &HeuristicResolver::resolveDeclRefExpr, |
533 | IM: dependentScopeDeclRefExpr(hasDependentName(N: "getPointer" )).bind(ID: "input" )); |
534 | } |
535 | |
536 | TEST(HeuristicResolver, DeclRefExpr_Nested) { |
537 | std::string Code = R"cpp( |
538 | struct S { |
539 | static int Waldo; |
540 | }; |
541 | template <typename T> |
542 | struct Meta { |
543 | using Type = S; |
544 | }; |
545 | template <typename T> |
546 | void foo() { |
547 | Meta<T>::Type::Waldo; |
548 | } |
549 | )cpp" ; |
550 | // Test resolution of "Waldo" in "Meta<T>::Type::Waldo". |
551 | expectResolution( |
552 | Code, ResolveFn: &HeuristicResolver::resolveDeclRefExpr, |
553 | IM: dependentScopeDeclRefExpr(hasDependentName(N: "Waldo" )).bind(ID: "input" ), |
554 | OMS: varDecl(hasName(Name: "Waldo" )).bind(ID: "output" )); |
555 | } |
556 | |
557 | TEST(HeuristicResolver, DependentNameType) { |
558 | std::string Code = R"cpp( |
559 | template <typename> |
560 | struct A { |
561 | struct B {}; |
562 | }; |
563 | template <typename T> |
564 | void foo(typename A<T>::B); |
565 | )cpp" ; |
566 | // Tests resolution of "B" in "A<T>::B". |
567 | expectResolution( |
568 | Code, ResolveFn: &HeuristicResolver::resolveDependentNameType, |
569 | IM: functionDecl(hasParameter(N: 0, InnerMatcher: hasType(InnerMatcher: dependentNameType().bind(ID: "input" )))), |
570 | OMS: classTemplateDecl( |
571 | has(cxxRecordDecl(has(cxxRecordDecl(hasName(Name: "B" )).bind(ID: "output" )))))); |
572 | } |
573 | |
574 | TEST(HeuristicResolver, DependentNameType_Nested) { |
575 | std::string Code = R"cpp( |
576 | template <typename> |
577 | struct A { |
578 | struct B { |
579 | struct C {}; |
580 | }; |
581 | }; |
582 | template <typename T> |
583 | void foo(typename A<T>::B::C); |
584 | )cpp" ; |
585 | // Tests resolution of "C" in "A<T>::B::C". |
586 | expectResolution( |
587 | Code, ResolveFn: &HeuristicResolver::resolveDependentNameType, |
588 | IM: functionDecl(hasParameter(N: 0, InnerMatcher: hasType(InnerMatcher: dependentNameType().bind(ID: "input" )))), |
589 | OMS: classTemplateDecl(has(cxxRecordDecl(has( |
590 | cxxRecordDecl(has(cxxRecordDecl(hasName(Name: "C" )).bind(ID: "output" )))))))); |
591 | } |
592 | |
593 | TEST(HeuristicResolver, DependentNameType_Recursion) { |
594 | std::string Code = R"cpp( |
595 | template <int N> |
596 | struct Waldo { |
597 | using Type = typename Waldo<N - 1>::Type::Next; |
598 | }; |
599 | )cpp" ; |
600 | // Test resolution of "Next" in "typename Waldo<N - 1>::Type::Next". |
601 | // Here, we are testing that we do not get into an infinite recursion. |
602 | expectResolution(Code, ResolveFn: &HeuristicResolver::resolveDependentNameType, |
603 | IM: typeAliasDecl(hasType(InnerMatcher: dependentNameType().bind(ID: "input" )))); |
604 | } |
605 | |
606 | TEST(HeuristicResolver, DependentNameType_MutualRecursion) { |
607 | std::string Code = R"cpp( |
608 | template <int N> |
609 | struct Odd; |
610 | template <int N> |
611 | struct Even { |
612 | using Type = typename Odd<N - 1>::Type::Next; |
613 | }; |
614 | template <int N> |
615 | struct Odd { |
616 | using Type = typename Even<N - 1>::Type::Next; |
617 | }; |
618 | )cpp" ; |
619 | // Test resolution of "Next" in "typename Even<N - 1>::Type::Next". |
620 | // Similar to the above but we have two mutually recursive templates. |
621 | expectResolution( |
622 | Code, ResolveFn: &HeuristicResolver::resolveDependentNameType, |
623 | IM: classTemplateDecl(hasName(Name: "Odd" ), |
624 | has(cxxRecordDecl(has(typeAliasDecl( |
625 | hasType(InnerMatcher: dependentNameType().bind(ID: "input" )))))))); |
626 | } |
627 | |
628 | TEST(HeuristicResolver, NestedNameSpecifier) { |
629 | // Test resolution of "B" in "A<T>::B::C". |
630 | // Unlike the "C", the "B" does not get its own DependentNameTypeLoc node, |
631 | // so the resolution uses the NestedNameSpecifier as input. |
632 | std::string Code = R"cpp( |
633 | template <typename> |
634 | struct A { |
635 | struct B { |
636 | struct C {}; |
637 | }; |
638 | }; |
639 | template <typename T> |
640 | void foo(typename A<T>::B::C); |
641 | )cpp" ; |
642 | // Adapt the call to resolveNestedNameSpecifierToType() to the interface |
643 | // expected by expectResolution() (returning a vector of decls). |
644 | ResolveFnT<NestedNameSpecifier> ResolveFn = |
645 | [](const HeuristicResolver *H, |
646 | const NestedNameSpecifier *NNS) -> std::vector<const NamedDecl *> { |
647 | return {H->resolveNestedNameSpecifierToType(NNS)->getAsCXXRecordDecl()}; |
648 | }; |
649 | expectResolution(Code, ResolveFn, |
650 | IM: nestedNameSpecifier(hasPrefix(InnerMatcher: specifiesType(InnerMatcher: hasDeclaration( |
651 | InnerMatcher: classTemplateDecl(hasName(Name: "A" )))))) |
652 | .bind(ID: "input" ), |
653 | OMS: classTemplateDecl(has(cxxRecordDecl( |
654 | has(cxxRecordDecl(hasName(Name: "B" )).bind(ID: "output" )))))); |
655 | } |
656 | |
657 | TEST(HeuristicResolver, TemplateSpecializationType) { |
658 | std::string Code = R"cpp( |
659 | template <typename> |
660 | struct A { |
661 | template <typename> |
662 | struct B {}; |
663 | }; |
664 | template <typename T> |
665 | void foo(typename A<T>::template B<int>); |
666 | )cpp" ; |
667 | // Test resolution of "B" in "A<T>::template B<int>". |
668 | expectResolution(Code, ResolveFn: &HeuristicResolver::resolveTemplateSpecializationType, |
669 | IM: functionDecl(hasParameter(N: 0, InnerMatcher: hasType(InnerMatcher: type().bind(ID: "input" )))), |
670 | OMS: classTemplateDecl(has(cxxRecordDecl( |
671 | has(classTemplateDecl(hasName(Name: "B" )).bind(ID: "output" )))))); |
672 | } |
673 | |
674 | TEST(HeuristicResolver, DependentCall_NonMember) { |
675 | std::string Code = R"cpp( |
676 | template <typename T> |
677 | void nonmember(T); |
678 | template <typename T> |
679 | void bar(T t) { |
680 | nonmember(t); |
681 | } |
682 | )cpp" ; |
683 | // Test resolution of "nonmember" in "nonmember(t)". |
684 | expectResolution(Code, ResolveFn: &HeuristicResolver::resolveCalleeOfCallExpr, |
685 | IM: callExpr(callee(InnerMatcher: unresolvedLookupExpr(hasAnyDeclaration( |
686 | InnerMatcher: functionTemplateDecl(hasName(Name: "nonmember" )))))) |
687 | .bind(ID: "input" ), |
688 | OMS: functionTemplateDecl(hasName(Name: "nonmember" )).bind(ID: "output" )); |
689 | } |
690 | |
691 | TEST(HeuristicResolver, DependentCall_Member) { |
692 | std::string Code = R"cpp( |
693 | template <typename T> |
694 | struct A { |
695 | void member(T); |
696 | }; |
697 | template <typename T> |
698 | void bar(A<T> a, T t) { |
699 | a.member(t); |
700 | } |
701 | )cpp" ; |
702 | // Test resolution of "member" in "a.member(t)". |
703 | expectResolution( |
704 | Code, ResolveFn: &HeuristicResolver::resolveCalleeOfCallExpr, |
705 | IM: callExpr(callee(InnerMatcher: cxxDependentScopeMemberExpr(hasMemberName(N: "member" )))) |
706 | .bind(ID: "input" ), |
707 | OMS: cxxMethodDecl(hasName(Name: "member" )).bind(ID: "output" )); |
708 | } |
709 | |
710 | TEST(HeuristicResolver, DependentCall_StaticMember) { |
711 | std::string Code = R"cpp( |
712 | template <typename T> |
713 | struct A { |
714 | static void static_member(T); |
715 | }; |
716 | template <typename T> |
717 | void bar(T t) { |
718 | A<T>::static_member(t); |
719 | } |
720 | )cpp" ; |
721 | // Test resolution of "static_member" in "A<T>::static_member(t)". |
722 | expectResolution(Code, ResolveFn: &HeuristicResolver::resolveCalleeOfCallExpr, |
723 | IM: callExpr(callee(InnerMatcher: dependentScopeDeclRefExpr( |
724 | hasDependentName(N: "static_member" )))) |
725 | .bind(ID: "input" ), |
726 | OMS: cxxMethodDecl(hasName(Name: "static_member" )).bind(ID: "output" )); |
727 | } |
728 | |
729 | TEST(HeuristicResolver, DependentCall_Overload) { |
730 | std::string Code = R"cpp( |
731 | void overload(int); |
732 | void overload(double); |
733 | template <typename T> |
734 | void bar(T t) { |
735 | overload(t); |
736 | } |
737 | )cpp" ; |
738 | // Test resolution of "overload" in "overload(t)". Both overload should be |
739 | // found. |
740 | expectResolution(Code, ResolveFn: &HeuristicResolver::resolveCalleeOfCallExpr, |
741 | IM: callExpr(callee(InnerMatcher: unresolvedLookupExpr(hasAnyDeclaration( |
742 | InnerMatcher: functionDecl(hasName(Name: "overload" )))))) |
743 | .bind(ID: "input" ), |
744 | OMS: functionDecl(hasName(Name: "overload" ), |
745 | hasParameter(N: 0, InnerMatcher: hasType(InnerMatcher: asString(Name: "double" )))) |
746 | .bind(ID: "output" ), |
747 | OMS: functionDecl(hasName(Name: "overload" ), |
748 | hasParameter(N: 0, InnerMatcher: hasType(InnerMatcher: asString(Name: "int" )))) |
749 | .bind(ID: "output" )); |
750 | } |
751 | |
752 | TEST(HeuristicResolver, UsingValueDecl) { |
753 | std::string Code = R"cpp( |
754 | template <typename T> |
755 | struct Base { |
756 | void waldo(); |
757 | }; |
758 | template <typename T> |
759 | struct Derived : Base<T> { |
760 | using Base<T>::waldo; |
761 | }; |
762 | )cpp" ; |
763 | // Test resolution of "waldo" in "Base<T>::waldo". |
764 | expectResolution(Code, ResolveFn: &HeuristicResolver::resolveUsingValueDecl, |
765 | IM: unresolvedUsingValueDecl(hasName(Name: "waldo" )).bind(ID: "input" ), |
766 | OMS: cxxMethodDecl(hasName(Name: "waldo" )).bind(ID: "output" )); |
767 | } |
768 | |
769 | } // namespace |
770 | } // namespace clang |
771 | |