1 //===-- HeuristicResolverTests.cpp --------------------------*- C++ -*-----===//
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
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"
15 using namespace clang::ast_matchers
;
16 using testing::ElementsAre
;
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
);
30 template <typename InputNode
>
31 using ResolveFnT
= std::function
<std::vector
<const NamedDecl
*>(
32 const HeuristicResolver
*, const InputNode
*)>;
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
, {"-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");
50 auto OutputNodeMatches
= [&](auto &OutputMatcher
, auto &Actual
) {
51 auto OutputMatches
= match(OutputMatcher
, Ctx
);
52 if (OutputMatches
.size() != 1u)
54 const auto *ExpectedOutput
=
55 OutputMatches
[0].template getNodeAs
<NamedDecl
>("output");
58 return ExpectedOutput
== Actual
;
61 HeuristicResolver
H(Ctx
);
62 auto Results
= ResolveFn(&H
, Input
);
63 EXPECT_THAT(Results
, ElementsAre(matchAdapter(OMS
, OutputNodeMatches
)...));
66 // Wrapper for the above that accepts a HeuristicResolver member function
68 template <typename InputNode
, typename InputMatcher
, typename
... OutputMatchers
>
69 void expectResolution(llvm::StringRef Code
,
70 std::vector
<const NamedDecl
*> (
71 HeuristicResolver::*ResolveFn
)(const InputNode
*)
73 const InputMatcher
&IM
, const OutputMatchers
&...OMS
) {
74 expectResolution(Code
, ResolveFnT
<InputNode
>(std::mem_fn(ResolveFn
)), IM
,
78 TEST(HeuristicResolver
, MemberExpr
) {
79 std::string Code
= R
"cpp(
90 // Test resolution of "bar" in "arg.bar()".
92 Code
, &HeuristicResolver::resolveMemberExpr
,
93 cxxDependentScopeMemberExpr(hasMemberName("bar")).bind("input"),
94 cxxMethodDecl(hasName("bar")).bind("output"));
97 TEST(HeuristicResolver
, MemberExpr_Overloads
) {
98 std::string Code
= R
"cpp(
105 template <typename T, typename U>
106 void foo(S<T> arg, U u) {
110 // Test resolution of "bar" in "arg.bar(u)". Both overloads should be found.
112 Code
, &HeuristicResolver::resolveMemberExpr
,
113 cxxDependentScopeMemberExpr(hasMemberName("bar")).bind("input"),
114 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("int"))))
116 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("float"))))
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 {
126 template <typename T>
127 void test(unique_ptr<S<T>>& v) {
131 // Test resolution of "foo" in "v->foo()".
133 Code
, &HeuristicResolver::resolveMemberExpr
,
134 cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"),
135 cxxMethodDecl(hasName("foo")).bind("output"));
138 TEST(HeuristicResolver
, MemberExpr_SmartPointer_Qualified
) {
139 std::string Code
= R
"cpp(
140 template <typename> struct Waldo {
144 template <typename T> struct unique_ptr {
147 template <typename T>
148 void test(unique_ptr<const Waldo<T>>& w) {
153 Code
, &HeuristicResolver::resolveMemberExpr
,
154 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
155 cxxMethodDecl(hasName("find"), isConst()).bind("output"));
158 TEST(HeuristicResolver
, MemberExpr_Static_Qualified
) {
159 std::string Code
= R
"cpp(
160 template <typename T>
164 template <typename T>
165 void foo(const Waldo<T>& t) {
169 // Test resolution of "find" in "t.find()".
170 // The object being `const` should have no bearing on a call to a static
173 Code
, &HeuristicResolver::resolveMemberExpr
,
174 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
175 cxxMethodDecl(hasName("find")).bind("output"));
178 TEST(HeuristicResolver
, MemberExpr_AutoTypeDeduction1
) {
179 std::string Code
= R
"cpp(
180 template <typename T>
184 template <typename T>
191 Code
, &HeuristicResolver::resolveMemberExpr
,
192 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),
193 fieldDecl(hasName("waldo")).bind("output"));
196 TEST(HeuristicResolver
, MemberExpr_AutoTypeDeduction2
) {
197 std::string Code
= R
"cpp(
202 template <typename T>
206 template <typename T>
213 Code
, &HeuristicResolver::resolveMemberExpr
,
214 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),
215 fieldDecl(hasName("waldo")).bind("output"));
218 TEST(HeuristicResolver
, MemberExpr_Chained
) {
219 std::string Code
= R
"cpp(
220 struct A { void foo() {} };
221 template <typename T>
229 // Test resolution of "foo" in "func(1).foo()".
231 Code
, &HeuristicResolver::resolveMemberExpr
,
232 cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"),
233 cxxMethodDecl(hasName("foo")).bind("output"));
236 TEST(HeuristicResolver
, MemberExpr_ReferenceType
) {
237 std::string Code
= R
"cpp(
241 template <typename T>
245 template <typename T>
250 // Test resolution of "waldo" in "a.b.waldo".
252 Code
, &HeuristicResolver::resolveMemberExpr
,
253 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),
254 fieldDecl(hasName("waldo")).bind("output"));
257 TEST(HeuristicResolver
, MemberExpr_PointerType
) {
258 std::string Code
= R
"cpp(
262 template <typename T>
266 template <typename T>
271 // Test resolution of "waldo" in "a.b->waldo".
273 Code
, &HeuristicResolver::resolveMemberExpr
,
274 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),
275 fieldDecl(hasName("waldo")).bind("output"));
278 TEST(HeuristicResolver
, MemberExpr_TemplateArgs
) {
279 std::string Code
= R
"cpp(
282 template <typename T> T convert();
284 template <typename T>
286 Foo::k(T()).template convert<T>();
289 // Test resolution of "convert" in "Foo::k(T()).template convert<T>()".
291 Code
, &HeuristicResolver::resolveMemberExpr
,
292 cxxDependentScopeMemberExpr(hasMemberName("convert")).bind("input"),
293 functionTemplateDecl(hasName("convert")).bind("output"));
296 TEST(HeuristicResolver
, MemberExpr_TypeAlias
) {
297 std::string Code
= R
"cpp(
298 template <typename T>
302 template <typename T>
303 using Wally = Waldo<T>;
304 template <typename T>
305 void foo(Wally<T> w) {
309 // Test resolution of "find" in "w.find()".
311 Code
, &HeuristicResolver::resolveMemberExpr
,
312 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
313 cxxMethodDecl(hasName("find")).bind("output"));
316 TEST(HeuristicResolver
, MemberExpr_BaseClass_TypeAlias
) {
317 std::string Code
= R
"cpp(
318 template <typename T>
322 template <typename T>
323 using Wally = Waldo<T>;
324 template <typename T>
325 struct S : Wally<T> {
331 // Test resolution of "find" in "this->find()".
333 Code
, &HeuristicResolver::resolveMemberExpr
,
334 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
335 cxxMethodDecl(hasName("find")).bind("output"));
338 TEST(HeuristicResolver
, MemberExpr_Metafunction
) {
339 std::string Code
= R
"cpp(
340 template <typename T>
344 template <typename T>
346 using Type = Waldo<T>;
348 template <typename T>
349 void foo(typename MetaWaldo<T>::Type w) {
353 // Test resolution of "find" in "w.find()".
355 Code
, &HeuristicResolver::resolveMemberExpr
,
356 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
357 cxxMethodDecl(hasName("find")).bind("output"));
360 TEST(HeuristicResolver
, MemberExpr_Metafunction_Enumerator
) {
361 std::string Code
= R
"cpp(
362 enum class State { Hidden };
363 template <typename T>
367 template <typename T>
368 void foo(typename Meta<T>::Type t) {
372 // Test resolution of "Hidden" in "t.Hidden".
374 Code
, &HeuristicResolver::resolveMemberExpr
,
375 cxxDependentScopeMemberExpr(hasMemberName("Hidden")).bind("input"),
376 enumConstantDecl(hasName("Hidden")).bind("output"));
379 TEST(HeuristicResolver
, MemberExpr_DeducedNonTypeTemplateParameter
) {
380 std::string Code
= R
"cpp(
390 // Test resolution of "found" in "W.found".
392 Code
, &HeuristicResolver::resolveMemberExpr
,
393 cxxDependentScopeMemberExpr(hasMemberName("found")).bind("input"),
394 fieldDecl(hasName("found")).bind("output"));
397 TEST(HeuristicResolver
, DeclRefExpr_StaticMethod
) {
398 std::string Code
= R
"cpp(
399 template <typename T>
404 template <typename T>
409 // Test resolution of "bar" in "S<T>::bar()".
411 Code
, &HeuristicResolver::resolveDeclRefExpr
,
412 dependentScopeDeclRefExpr(hasDependentName("bar")).bind("input"),
413 cxxMethodDecl(hasName("bar")).bind("output"));
416 TEST(HeuristicResolver
, DeclRefExpr_StaticOverloads
) {
417 std::string Code
= R
"cpp(
418 template <typename T>
420 static void bar(int);
421 static void bar(float);
424 template <typename T, typename U>
429 // Test resolution of "bar" in "S<T>::bar(u)". Both overloads should be found.
431 Code
, &HeuristicResolver::resolveDeclRefExpr
,
432 dependentScopeDeclRefExpr(hasDependentName("bar")).bind("input"),
433 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("int"))))
435 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("float"))))
439 TEST(HeuristicResolver
, DeclRefExpr_Enumerator
) {
440 std::string Code
= R
"cpp(
441 template <typename T>
443 enum class E { A, B };
447 // Test resolution of "A" in "E::A".
449 Code
, &HeuristicResolver::resolveDeclRefExpr
,
450 dependentScopeDeclRefExpr(hasDependentName("A")).bind("input"),
451 enumConstantDecl(hasName("A")).bind("output"));
454 TEST(HeuristicResolver
, DeclRefExpr_RespectScope
) {
455 std::string Code
= R
"cpp(
456 template <typename Info>
457 struct PointerIntPair {
458 void *getPointer() const { return Info::getPointer(); }
461 // Test resolution of "getPointer" in "Info::getPointer()".
462 // Here, we are testing that we do not incorrectly get the enclosing
463 // getPointer() function as a result.
465 Code
, &HeuristicResolver::resolveDeclRefExpr
,
466 dependentScopeDeclRefExpr(hasDependentName("getPointer")).bind("input"));
469 TEST(HeuristicResolver
, DeclRefExpr_Nested
) {
470 std::string Code
= R
"cpp(
474 template <typename T>
478 template <typename T>
480 Meta<T>::Type::Waldo;
483 // Test resolution of "Waldo" in "Meta<T>::Type::Waldo".
485 Code
, &HeuristicResolver::resolveDeclRefExpr
,
486 dependentScopeDeclRefExpr(hasDependentName("Waldo")).bind("input"),
487 varDecl(hasName("Waldo")).bind("output"));
490 TEST(HeuristicResolver
, DependentNameType
) {
491 std::string Code
= R
"cpp(
496 template <typename T>
497 void foo(typename A<T>::B);
499 // Tests resolution of "B" in "A<T>::B".
501 Code
, &HeuristicResolver::resolveDependentNameType
,
502 functionDecl(hasParameter(0, hasType(dependentNameType().bind("input")))),
504 has(cxxRecordDecl(has(cxxRecordDecl(hasName("B")).bind("output"))))));
507 TEST(HeuristicResolver
, DependentNameType_Nested
) {
508 std::string Code
= R
"cpp(
515 template <typename T>
516 void foo(typename A<T>::B::C);
518 // Tests resolution of "C" in "A<T>::B::C".
520 Code
, &HeuristicResolver::resolveDependentNameType
,
521 functionDecl(hasParameter(0, hasType(dependentNameType().bind("input")))),
522 classTemplateDecl(has(cxxRecordDecl(has(
523 cxxRecordDecl(has(cxxRecordDecl(hasName("C")).bind("output"))))))));
526 TEST(HeuristicResolver
, DependentNameType_Recursion
) {
527 std::string Code
= R
"cpp(
530 using Type = typename Waldo<N - 1>::Type::Next;
533 // Test resolution of "Next" in "typename Waldo<N - 1>::Type::Next".
534 // Here, we are testing that we do not get into an infinite recursion.
535 expectResolution(Code
, &HeuristicResolver::resolveDependentNameType
,
536 typeAliasDecl(hasType(dependentNameType().bind("input"))));
539 TEST(HeuristicResolver
, DependentNameType_MutualRecursion
) {
540 std::string Code
= R
"cpp(
545 using Type = typename Odd<N - 1>::Type::Next;
549 using Type = typename Even<N - 1>::Type::Next;
552 // Test resolution of "Next" in "typename Even<N - 1>::Type::Next".
553 // Similar to the above but we have two mutually recursive templates.
555 Code
, &HeuristicResolver::resolveDependentNameType
,
556 classTemplateDecl(hasName("Odd"),
557 has(cxxRecordDecl(has(typeAliasDecl(
558 hasType(dependentNameType().bind("input"))))))));
561 TEST(HeuristicResolver
, NestedNameSpecifier
) {
562 // Test resolution of "B" in "A<T>::B::C".
563 // Unlike the "C", the "B" does not get its own DependentNameTypeLoc node,
564 // so the resolution uses the NestedNameSpecifier as input.
565 std::string Code
= R
"cpp(
572 template <typename T>
573 void foo(typename A<T>::B::C);
575 // Adapt the call to resolveNestedNameSpecifierToType() to the interface
576 // expected by expectResolution() (returning a vector of decls).
577 ResolveFnT
<NestedNameSpecifier
> ResolveFn
=
578 [](const HeuristicResolver
*H
,
579 const NestedNameSpecifier
*NNS
) -> std::vector
<const NamedDecl
*> {
580 return {H
->resolveNestedNameSpecifierToType(NNS
)->getAsCXXRecordDecl()};
582 expectResolution(Code
, ResolveFn
,
583 nestedNameSpecifier(hasPrefix(specifiesType(hasDeclaration(
584 classTemplateDecl(hasName("A"))))))
586 classTemplateDecl(has(cxxRecordDecl(
587 has(cxxRecordDecl(hasName("B")).bind("output"))))));
590 TEST(HeuristicResolver
, TemplateSpecializationType
) {
591 std::string Code
= R
"cpp(
597 template <typename T>
598 void foo(typename A<T>::template B<int>);
600 // Test resolution of "B" in "A<T>::template B<int>".
601 expectResolution(Code
, &HeuristicResolver::resolveTemplateSpecializationType
,
602 functionDecl(hasParameter(0, hasType(type().bind("input")))),
603 classTemplateDecl(has(cxxRecordDecl(
604 has(classTemplateDecl(hasName("B")).bind("output"))))));
607 TEST(HeuristicResolver
, DependentCall_NonMember
) {
608 std::string Code
= R
"cpp(
609 template <typename T>
611 template <typename T>
616 // Test resolution of "nonmember" in "nonmember(t)".
617 expectResolution(Code
, &HeuristicResolver::resolveCalleeOfCallExpr
,
618 callExpr(callee(unresolvedLookupExpr(hasAnyDeclaration(
619 functionTemplateDecl(hasName("nonmember"))))))
621 functionTemplateDecl(hasName("nonmember")).bind("output"));
624 TEST(HeuristicResolver
, DependentCall_Member
) {
625 std::string Code
= R
"cpp(
626 template <typename T>
630 template <typename T>
631 void bar(A<T> a, T t) {
635 // Test resolution of "member" in "a.member(t)".
637 Code
, &HeuristicResolver::resolveCalleeOfCallExpr
,
638 callExpr(callee(cxxDependentScopeMemberExpr(hasMemberName("member"))))
640 cxxMethodDecl(hasName("member")).bind("output"));
643 TEST(HeuristicResolver
, DependentCall_StaticMember
) {
644 std::string Code
= R
"cpp(
645 template <typename T>
647 static void static_member(T);
649 template <typename T>
651 A<T>::static_member(t);
654 // Test resolution of "static_member" in "A<T>::static_member(t)".
655 expectResolution(Code
, &HeuristicResolver::resolveCalleeOfCallExpr
,
656 callExpr(callee(dependentScopeDeclRefExpr(
657 hasDependentName("static_member"))))
659 cxxMethodDecl(hasName("static_member")).bind("output"));
662 TEST(HeuristicResolver
, DependentCall_Overload
) {
663 std::string Code
= R
"cpp(
665 void overload(double);
666 template <typename T>
671 // Test resolution of "overload" in "overload(t)". Both overload should be
673 expectResolution(Code
, &HeuristicResolver::resolveCalleeOfCallExpr
,
674 callExpr(callee(unresolvedLookupExpr(hasAnyDeclaration(
675 functionDecl(hasName("overload"))))))
677 functionDecl(hasName("overload"),
678 hasParameter(0, hasType(asString("double"))))
680 functionDecl(hasName("overload"),
681 hasParameter(0, hasType(asString("int"))))
685 TEST(HeuristicResolver
, UsingValueDecl
) {
686 std::string Code
= R
"cpp(
687 template <typename T>
691 template <typename T>
692 struct Derived : Base<T> {
693 using Base<T>::waldo;
696 // Test resolution of "waldo" in "Base<T>::waldo".
697 expectResolution(Code
, &HeuristicResolver::resolveUsingValueDecl
,
698 unresolvedUsingValueDecl(hasName("waldo")).bind("input"),
699 cxxMethodDecl(hasName("waldo")).bind("output"));