xref: /llvm-project/clang/unittests/Sema/HeuristicResolverTest.cpp (revision a4d17c44f14984f8f031d0715ea4d1387e96b741)
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, {"-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, &HeuristicResolver::resolveMemberExpr,
93       cxxDependentScopeMemberExpr(hasMemberName("bar")).bind("input"),
94       cxxMethodDecl(hasName("bar")).bind("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, &HeuristicResolver::resolveMemberExpr,
113       cxxDependentScopeMemberExpr(hasMemberName("bar")).bind("input"),
114       cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("int"))))
115           .bind("output"),
116       cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("float"))))
117           .bind("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, &HeuristicResolver::resolveMemberExpr,
134       cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"),
135       cxxMethodDecl(hasName("foo")).bind("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, &HeuristicResolver::resolveMemberExpr,
154       cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
155       cxxMethodDecl(hasName("find"), isConst()).bind("output"));
156 }
157 
158 TEST(HeuristicResolver, MemberExpr_AutoTypeDeduction1) {
159   std::string Code = R"cpp(
160     template <typename T>
161     struct A {
162       int waldo;
163     };
164     template <typename T>
165     void foo(A<T> a) {
166       auto copy = a;
167       copy.waldo;
168     }
169   )cpp";
170   expectResolution(
171       Code, &HeuristicResolver::resolveMemberExpr,
172       cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),
173       fieldDecl(hasName("waldo")).bind("output"));
174 }
175 
176 TEST(HeuristicResolver, MemberExpr_AutoTypeDeduction2) {
177   std::string Code = R"cpp(
178     struct B {
179       int waldo;
180     };
181 
182     template <typename T>
183     struct A {
184       B b;
185     };
186     template <typename T>
187     void foo(A<T> a) {
188       auto b = a.b;
189       b.waldo;
190     }
191   )cpp";
192   expectResolution(
193       Code, &HeuristicResolver::resolveMemberExpr,
194       cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),
195       fieldDecl(hasName("waldo")).bind("output"));
196 }
197 
198 TEST(HeuristicResolver, MemberExpr_Chained) {
199   std::string Code = R"cpp(
200     struct A { void foo() {} };
201     template <typename T>
202     struct B {
203       A func(int);
204       void bar() {
205         func(1).foo();
206       }
207     };
208   )cpp";
209   // Test resolution of "foo" in "func(1).foo()".
210   expectResolution(
211       Code, &HeuristicResolver::resolveMemberExpr,
212       cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"),
213       cxxMethodDecl(hasName("foo")).bind("output"));
214 }
215 
216 TEST(HeuristicResolver, MemberExpr_TemplateArgs) {
217   std::string Code = R"cpp(
218     struct Foo {
219       static Foo k(int);
220       template <typename T> T convert();
221     };
222     template <typename T>
223     void test() {
224       Foo::k(T()).template convert<T>();
225     }
226   )cpp";
227   // Test resolution of "convert" in "Foo::k(T()).template convert<T>()".
228   expectResolution(
229       Code, &HeuristicResolver::resolveMemberExpr,
230       cxxDependentScopeMemberExpr(hasMemberName("convert")).bind("input"),
231       functionTemplateDecl(hasName("convert")).bind("output"));
232 }
233 
234 TEST(HeuristicResolver, MemberExpr_TypeAlias) {
235   std::string Code = R"cpp(
236     template <typename T>
237     struct Waldo {
238       void find();
239     };
240     template <typename T>
241     using Wally = Waldo<T>;
242     template <typename T>
243     void foo(Wally<T> w) {
244       w.find();
245     }
246   )cpp";
247   // Test resolution of "find" in "w.find()".
248   expectResolution(
249       Code, &HeuristicResolver::resolveMemberExpr,
250       cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
251       cxxMethodDecl(hasName("find")).bind("output"));
252 }
253 
254 TEST(HeuristicResolver, MemberExpr_BaseClass_TypeAlias) {
255   std::string Code = R"cpp(
256     template <typename T>
257     struct Waldo {
258       void find();
259     };
260     template <typename T>
261     using Wally = Waldo<T>;
262     template <typename T>
263     struct S : Wally<T> {
264       void foo() {
265         this->find();
266       }
267     };
268   )cpp";
269   // Test resolution of "find" in "this->find()".
270   expectResolution(
271       Code, &HeuristicResolver::resolveMemberExpr,
272       cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
273       cxxMethodDecl(hasName("find")).bind("output"));
274 }
275 
276 TEST(HeuristicResolver, MemberExpr_Metafunction) {
277   std::string Code = R"cpp(
278     template <typename T>
279     struct Waldo {
280       void find();
281     };
282     template <typename T>
283     struct MetaWaldo {
284       using Type = Waldo<T>;
285     };
286     template <typename T>
287     void foo(typename MetaWaldo<T>::Type w) {
288       w.find();
289     }
290   )cpp";
291   // Test resolution of "find" in "w.find()".
292   expectResolution(
293       Code, &HeuristicResolver::resolveMemberExpr,
294       cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),
295       cxxMethodDecl(hasName("find")).bind("output"));
296 }
297 
298 TEST(HeuristicResolver, MemberExpr_Metafunction_Enumerator) {
299   std::string Code = R"cpp(
300     enum class State { Hidden };
301     template <typename T>
302     struct Meta {
303       using Type = State;
304     };
305     template <typename T>
306     void foo(typename Meta<T>::Type t) {
307       t.Hidden;
308     }
309   )cpp";
310   // Test resolution of "Hidden" in "t.Hidden".
311   expectResolution(
312       Code, &HeuristicResolver::resolveMemberExpr,
313       cxxDependentScopeMemberExpr(hasMemberName("Hidden")).bind("input"),
314       enumConstantDecl(hasName("Hidden")).bind("output"));
315 }
316 
317 TEST(HeuristicResolver, MemberExpr_DeducedNonTypeTemplateParameter) {
318   std::string Code = R"cpp(
319     template <int N>
320     struct Waldo {
321       const int found = N;
322     };
323     template <Waldo W>
324     int foo() {
325       return W.found;
326     }
327   )cpp";
328   // Test resolution of "found" in "W.found".
329   expectResolution(
330       Code, &HeuristicResolver::resolveMemberExpr,
331       cxxDependentScopeMemberExpr(hasMemberName("found")).bind("input"),
332       fieldDecl(hasName("found")).bind("output"));
333 }
334 
335 TEST(HeuristicResolver, DeclRefExpr_StaticMethod) {
336   std::string Code = R"cpp(
337     template <typename T>
338     struct S {
339       static void bar() {}
340     };
341 
342     template <typename T>
343     void foo() {
344       S<T>::bar();
345     }
346   )cpp";
347   // Test resolution of "bar" in "S<T>::bar()".
348   expectResolution(
349       Code, &HeuristicResolver::resolveDeclRefExpr,
350       dependentScopeDeclRefExpr(hasDependentName("bar")).bind("input"),
351       cxxMethodDecl(hasName("bar")).bind("output"));
352 }
353 
354 TEST(HeuristicResolver, DeclRefExpr_StaticOverloads) {
355   std::string Code = R"cpp(
356     template <typename T>
357     struct S {
358       static void bar(int);
359       static void bar(float);
360     };
361 
362     template <typename T, typename U>
363     void foo(U u) {
364       S<T>::bar(u);
365     }
366   )cpp";
367   // Test resolution of "bar" in "S<T>::bar(u)". Both overloads should be found.
368   expectResolution(
369       Code, &HeuristicResolver::resolveDeclRefExpr,
370       dependentScopeDeclRefExpr(hasDependentName("bar")).bind("input"),
371       cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("int"))))
372           .bind("output"),
373       cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("float"))))
374           .bind("output"));
375 }
376 
377 TEST(HeuristicResolver, DeclRefExpr_Enumerator) {
378   std::string Code = R"cpp(
379     template <typename T>
380     struct Foo {
381       enum class E { A, B };
382       E e = E::A;
383     };
384   )cpp";
385   // Test resolution of "A" in "E::A".
386   expectResolution(
387       Code, &HeuristicResolver::resolveDeclRefExpr,
388       dependentScopeDeclRefExpr(hasDependentName("A")).bind("input"),
389       enumConstantDecl(hasName("A")).bind("output"));
390 }
391 
392 TEST(HeuristicResolver, DeclRefExpr_RespectScope) {
393   std::string Code = R"cpp(
394     template <typename Info>
395     struct PointerIntPair {
396       void *getPointer() const { return Info::getPointer(); }
397     };
398   )cpp";
399   // Test resolution of "getPointer" in "Info::getPointer()".
400   // Here, we are testing that we do not incorrectly get the enclosing
401   // getPointer() function as a result.
402   expectResolution(
403       Code, &HeuristicResolver::resolveDeclRefExpr,
404       dependentScopeDeclRefExpr(hasDependentName("getPointer")).bind("input"));
405 }
406 
407 TEST(HeuristicResolver, DeclRefExpr_Nested) {
408   std::string Code = R"cpp(
409     struct S {
410       static int Waldo;
411     };
412     template <typename T>
413     struct Meta {
414       using Type = S;
415     };
416     template <typename T>
417     void foo() {
418       Meta<T>::Type::Waldo;
419     }
420   )cpp";
421   // Test resolution of "Waldo" in "Meta<T>::Type::Waldo".
422   expectResolution(
423       Code, &HeuristicResolver::resolveDeclRefExpr,
424       dependentScopeDeclRefExpr(hasDependentName("Waldo")).bind("input"),
425       varDecl(hasName("Waldo")).bind("output"));
426 }
427 
428 TEST(HeuristicResolver, DependentNameType) {
429   std::string Code = R"cpp(
430     template <typename>
431     struct A {
432       struct B {};
433     };
434     template <typename T>
435     void foo(typename A<T>::B);
436   )cpp";
437   // Tests resolution of "B" in "A<T>::B".
438   expectResolution(
439       Code, &HeuristicResolver::resolveDependentNameType,
440       functionDecl(hasParameter(0, hasType(dependentNameType().bind("input")))),
441       classTemplateDecl(
442           has(cxxRecordDecl(has(cxxRecordDecl(hasName("B")).bind("output"))))));
443 }
444 
445 TEST(HeuristicResolver, DependentNameType_Nested) {
446   std::string Code = R"cpp(
447     template <typename>
448     struct A {
449       struct B {
450         struct C {};
451       };
452     };
453     template <typename T>
454     void foo(typename A<T>::B::C);
455   )cpp";
456   // Tests resolution of "C" in "A<T>::B::C".
457   expectResolution(
458       Code, &HeuristicResolver::resolveDependentNameType,
459       functionDecl(hasParameter(0, hasType(dependentNameType().bind("input")))),
460       classTemplateDecl(has(cxxRecordDecl(has(
461           cxxRecordDecl(has(cxxRecordDecl(hasName("C")).bind("output"))))))));
462 }
463 
464 TEST(HeuristicResolver, DependentNameType_Recursion) {
465   std::string Code = R"cpp(
466     template <int N>
467     struct Waldo {
468       using Type = typename Waldo<N - 1>::Type::Next;
469     };
470   )cpp";
471   // Test resolution of "Next" in "typename Waldo<N - 1>::Type::Next".
472   // Here, we are testing that we do not get into an infinite recursion.
473   expectResolution(Code, &HeuristicResolver::resolveDependentNameType,
474                    typeAliasDecl(hasType(dependentNameType().bind("input"))));
475 }
476 
477 TEST(HeuristicResolver, DependentNameType_MutualRecursion) {
478   std::string Code = R"cpp(
479     template <int N>
480     struct Odd;
481     template <int N>
482     struct Even {
483       using Type = typename Odd<N - 1>::Type::Next;
484     };
485     template <int N>
486     struct Odd {
487       using Type = typename Even<N - 1>::Type::Next;
488     };
489   )cpp";
490   // Test resolution of "Next" in "typename Even<N - 1>::Type::Next".
491   // Similar to the above but we have two mutually recursive templates.
492   expectResolution(
493       Code, &HeuristicResolver::resolveDependentNameType,
494       classTemplateDecl(hasName("Odd"),
495                         has(cxxRecordDecl(has(typeAliasDecl(
496                             hasType(dependentNameType().bind("input"))))))));
497 }
498 
499 TEST(HeuristicResolver, NestedNameSpecifier) {
500   // Test resolution of "B" in "A<T>::B::C".
501   // Unlike the "C", the "B" does not get its own DependentNameTypeLoc node,
502   // so the resolution uses the NestedNameSpecifier as input.
503   std::string Code = R"cpp(
504     template <typename>
505     struct A {
506       struct B {
507         struct C {};
508       };
509     };
510     template <typename T>
511     void foo(typename A<T>::B::C);
512   )cpp";
513   // Adapt the call to resolveNestedNameSpecifierToType() to the interface
514   // expected by expectResolution() (returning a vector of decls).
515   ResolveFnT<NestedNameSpecifier> ResolveFn =
516       [](const HeuristicResolver *H,
517          const NestedNameSpecifier *NNS) -> std::vector<const NamedDecl *> {
518     return {H->resolveNestedNameSpecifierToType(NNS)->getAsCXXRecordDecl()};
519   };
520   expectResolution(Code, ResolveFn,
521                    nestedNameSpecifier(hasPrefix(specifiesType(hasDeclaration(
522                                            classTemplateDecl(hasName("A"))))))
523                        .bind("input"),
524                    classTemplateDecl(has(cxxRecordDecl(
525                        has(cxxRecordDecl(hasName("B")).bind("output"))))));
526 }
527 
528 TEST(HeuristicResolver, TemplateSpecializationType) {
529   std::string Code = R"cpp(
530     template <typename>
531     struct A {
532       template <typename>
533       struct B {};
534     };
535     template <typename T>
536     void foo(typename A<T>::template B<int>);
537   )cpp";
538   // Test resolution of "B" in "A<T>::template B<int>".
539   expectResolution(Code, &HeuristicResolver::resolveTemplateSpecializationType,
540                    functionDecl(hasParameter(0, hasType(type().bind("input")))),
541                    classTemplateDecl(has(cxxRecordDecl(
542                        has(classTemplateDecl(hasName("B")).bind("output"))))));
543 }
544 
545 TEST(HeuristicResolver, DependentCall_NonMember) {
546   std::string Code = R"cpp(
547     template <typename T>
548     void nonmember(T);
549     template <typename T>
550     void bar(T t) {
551       nonmember(t);
552     }
553   )cpp";
554   // Test resolution of "nonmember" in "nonmember(t)".
555   expectResolution(Code, &HeuristicResolver::resolveCalleeOfCallExpr,
556                    callExpr(callee(unresolvedLookupExpr(hasAnyDeclaration(
557                                 functionTemplateDecl(hasName("nonmember"))))))
558                        .bind("input"),
559                    functionTemplateDecl(hasName("nonmember")).bind("output"));
560 }
561 
562 TEST(HeuristicResolver, DependentCall_Member) {
563   std::string Code = R"cpp(
564     template <typename T>
565     struct A {
566       void member(T);
567     };
568     template <typename T>
569     void bar(A<T> a, T t) {
570       a.member(t);
571     }
572   )cpp";
573   // Test resolution of "member" in "a.member(t)".
574   expectResolution(
575       Code, &HeuristicResolver::resolveCalleeOfCallExpr,
576       callExpr(callee(cxxDependentScopeMemberExpr(hasMemberName("member"))))
577           .bind("input"),
578       cxxMethodDecl(hasName("member")).bind("output"));
579 }
580 
581 TEST(HeuristicResolver, DependentCall_StaticMember) {
582   std::string Code = R"cpp(
583     template <typename T>
584     struct A {
585       static void static_member(T);
586     };
587     template <typename T>
588     void bar(T t) {
589       A<T>::static_member(t);
590     }
591   )cpp";
592   // Test resolution of "static_member" in "A<T>::static_member(t)".
593   expectResolution(Code, &HeuristicResolver::resolveCalleeOfCallExpr,
594                    callExpr(callee(dependentScopeDeclRefExpr(
595                                 hasDependentName("static_member"))))
596                        .bind("input"),
597                    cxxMethodDecl(hasName("static_member")).bind("output"));
598 }
599 
600 TEST(HeuristicResolver, DependentCall_Overload) {
601   std::string Code = R"cpp(
602     void overload(int);
603     void overload(double);
604     template <typename T>
605     void bar(T t) {
606       overload(t);
607     }
608   )cpp";
609   // Test resolution of "overload" in "overload(t)". Both overload should be
610   // found.
611   expectResolution(Code, &HeuristicResolver::resolveCalleeOfCallExpr,
612                    callExpr(callee(unresolvedLookupExpr(hasAnyDeclaration(
613                                 functionDecl(hasName("overload"))))))
614                        .bind("input"),
615                    functionDecl(hasName("overload"),
616                                 hasParameter(0, hasType(asString("double"))))
617                        .bind("output"),
618                    functionDecl(hasName("overload"),
619                                 hasParameter(0, hasType(asString("int"))))
620                        .bind("output"));
621 }
622 
623 TEST(HeuristicResolver, UsingValueDecl) {
624   std::string Code = R"cpp(
625     template <typename T>
626     struct Base {
627       void waldo();
628     };
629     template <typename T>
630     struct Derived : Base<T> {
631       using Base<T>::waldo;
632     };
633   )cpp";
634   // Test resolution of "waldo" in "Base<T>::waldo".
635   expectResolution(Code, &HeuristicResolver::resolveUsingValueDecl,
636                    unresolvedUsingValueDecl(hasName("waldo")).bind("input"),
637                    cxxMethodDecl(hasName("waldo")).bind("output"));
638 }
639 
640 } // namespace
641 } // namespace clang
642