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
15using namespace clang::ast_matchers;
16using testing::ElementsAre;
17
18namespace clang {
19namespace {
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.
26MATCHER_P2(matchAdapter, MatcherForElement, MatchFunction, "matchAdapter") {
27 return MatchFunction(MatcherForElement, arg);
28}
29
30template <typename InputNode>
31using 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".
40template <typename InputNode, typename InputMatcher, typename... OutputMatchers>
41void 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.
68template <typename InputNode, typename InputMatcher, typename... OutputMatchers>
69void 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
78TEST(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
97TEST(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
120TEST(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
138TEST(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
158TEST(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
178TEST(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
196TEST(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
218TEST(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
236TEST(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
257TEST(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
278TEST(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
296TEST(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
316TEST(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
338TEST(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
360TEST(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
379TEST(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
397TEST(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
413TEST(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
430TEST(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
447TEST(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
466TEST(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
483TEST(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
506TEST(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
521TEST(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
536TEST(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
557TEST(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
574TEST(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
593TEST(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
606TEST(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
628TEST(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
657TEST(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
674TEST(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
691TEST(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
710TEST(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
729TEST(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
752TEST(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

source code of clang/unittests/Sema/HeuristicResolverTest.cpp