1023 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8#include "clang/Sema/HeuristicResolver.h"9#include "clang/ASTMatchers/ASTMatchFinder.h"10#include "clang/ASTMatchers/ASTMatchers.h"11#include "clang/Basic/Diagnostic.h"12#include "clang/Tooling/Tooling.h"13#include "gmock/gmock-matchers.h"14#include "gtest/gtest.h"15 16using namespace clang::ast_matchers;17using testing::ElementsAre;18 19namespace clang {20namespace {21 22// Helper for matching a sequence of elements with a variadic list of matchers.23// Usage: `ElementsAre(matchAdapter(Vs, MatchFunction)...)`, where `Vs...` is24// a variadic list of matchers.25// For each `V` in `Vs`, this will match the corresponding element `E` if26// `MatchFunction(V, E)` is true.27MATCHER_P2(matchAdapter, MatcherForElement, MatchFunction, "matchAdapter") {28 return MatchFunction(MatcherForElement, arg);29}30 31template <typename InputNode>32using ResolveFnT = std::function<std::vector<const NamedDecl *>(33 const HeuristicResolver *, InputNode)>;34 35// Test heuristic resolution on `Code` using the resolution procedure36// `ResolveFn`, which takes a `HeuristicResolver` and an input AST node of type37// `InputNode` and returns a `std::vector<const NamedDecl *>`.38// `InputMatcher` should be an AST matcher that matches a single node to pass as39// input to `ResolveFn`, bound to the ID "input". `OutputMatchers` should be AST40// matchers that each match a single node, bound to the ID "output".41template <typename InputNode, typename ParamT, typename InputMatcher,42 typename... OutputMatchers>43void expectResolution(llvm::StringRef Code, ResolveFnT<ParamT> ResolveFn,44 const InputMatcher &IM, const OutputMatchers &...OMS) {45 auto TU = tooling::buildASTFromCodeWithArgs(46 Code, {"-std=c++23"}, "input.cc", "clang-tool",47 std::make_shared<PCHContainerOperations>(),48 tooling::getClangStripDependencyFileAdjuster(),49 tooling::FileContentMappings(), nullptr, llvm::vfs::getRealFileSystem(),50 CaptureDiagsKind::All);51 52 for (const auto &D : TU->storedDiagnostics()) {53 EXPECT_TRUE(D.getLevel() < DiagnosticsEngine::Error)54 << "Unexpected error diagnostic while building AST for test code: "55 << D.getMessage();56 }57 58 auto &Ctx = TU->getASTContext();59 auto InputMatches = match(IM, Ctx);60 ASSERT_EQ(1u, InputMatches.size());61 const auto *Input = InputMatches[0].template getNodeAs<InputNode>("input");62 ASSERT_TRUE(Input);63 64 auto OutputNodeMatches = [&](auto &OutputMatcher, auto &Actual) {65 auto OutputMatches = match(OutputMatcher, Ctx);66 if (OutputMatches.size() != 1u)67 return false;68 const auto *ExpectedOutput =69 OutputMatches[0].template getNodeAs<NamedDecl>("output");70 if (!ExpectedOutput)71 return false;72 return ExpectedOutput == Actual;73 };74 75 HeuristicResolver H(Ctx);76 std::vector<const NamedDecl *> Results;77 if constexpr (std::is_pointer_v<ParamT>)78 Results = ResolveFn(&H, Input);79 else80 Results = ResolveFn(&H, *Input);81 EXPECT_THAT(Results, ElementsAre(matchAdapter(OMS, OutputNodeMatches)...));82}83 84// Wrapper for the above that accepts a HeuristicResolver member function85// pointer directly.86template <typename InputNode, typename InputMatcher, typename... OutputMatchers>87void expectResolution(llvm::StringRef Code,88 std::vector<const NamedDecl *> (89 HeuristicResolver::*ResolveFn)(const InputNode *)90 const,91 const InputMatcher &IM, const OutputMatchers &...OMS) {92 expectResolution<InputNode>(93 Code, ResolveFnT<const InputNode *>(std::mem_fn(ResolveFn)), IM, OMS...);94}95 96TEST(HeuristicResolver, MemberExpr) {97 std::string Code = R"cpp(98 template <typename T>99 struct S {100 void bar() {}101 };102 103 template <typename T>104 void foo(S<T> arg) {105 arg.bar();106 }107 )cpp";108 // Test resolution of "bar" in "arg.bar()".109 expectResolution(110 Code, &HeuristicResolver::resolveMemberExpr,111 cxxDependentScopeMemberExpr(hasMemberName("bar")).bind("input"),112 cxxMethodDecl(hasName("bar")).bind("output"));113}114 115TEST(HeuristicResolver, MemberExpr_Overloads) {116 std::string Code = R"cpp(117 template <typename T>118 struct S {119 void bar(int);120 void bar(float);121 };122 123 template <typename T, typename U>124 void foo(S<T> arg, U u) {125 arg.bar(u);126 }127 )cpp";128 // Test resolution of "bar" in "arg.bar(u)". Both overloads should be found.129 expectResolution(130 Code, &HeuristicResolver::resolveMemberExpr,131 cxxDependentScopeMemberExpr(hasMemberName("bar")).bind("input"),132 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("int"))))133 .bind("output"),134 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("float"))))135 .bind("output"));136}137 138TEST(HeuristicResolver, MemberExpr_SmartPointer) {139 std::string Code = R"cpp(140 template <typename> struct S { void foo() {} };141 template <typename T> struct unique_ptr {142 T* operator->();143 };144 template <typename T>145 void test(unique_ptr<S<T>>& v) {146 v->foo();147 }148 )cpp";149 // Test resolution of "foo" in "v->foo()".150 expectResolution(151 Code, &HeuristicResolver::resolveMemberExpr,152 cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"),153 cxxMethodDecl(hasName("foo")).bind("output"));154}155 156TEST(HeuristicResolver, MemberExpr_SmartPointer_Qualified) {157 std::string Code = R"cpp(158 template <typename> struct Waldo {159 void find();160 void find() const;161 };162 template <typename T> struct unique_ptr {163 T* operator->();164 };165 template <typename T>166 void test(unique_ptr<const Waldo<T>>& w) {167 w->find();168 }169 )cpp";170 expectResolution(171 Code, &HeuristicResolver::resolveMemberExpr,172 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),173 cxxMethodDecl(hasName("find"), isConst()).bind("output"));174}175 176TEST(HeuristicResolver, MemberExpr_Static_Qualified) {177 std::string Code = R"cpp(178 template <typename T>179 struct Waldo {180 static void find();181 };182 template <typename T>183 void foo(const Waldo<T>& t) {184 t.find();185 }186 )cpp";187 // Test resolution of "find" in "t.find()".188 // The object being `const` should have no bearing on a call to a static189 // method.190 expectResolution(191 Code, &HeuristicResolver::resolveMemberExpr,192 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),193 cxxMethodDecl(hasName("find")).bind("output"));194}195 196TEST(HeuristicResolver, MemberExpr_AutoTypeDeduction1) {197 std::string Code = R"cpp(198 template <typename T>199 struct A {200 int waldo;201 };202 template <typename T>203 void foo(A<T> a) {204 auto copy = a;205 copy.waldo;206 }207 )cpp";208 expectResolution(209 Code, &HeuristicResolver::resolveMemberExpr,210 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),211 fieldDecl(hasName("waldo")).bind("output"));212}213 214TEST(HeuristicResolver, MemberExpr_AutoTypeDeduction2) {215 std::string Code = R"cpp(216 struct B {217 int waldo;218 };219 template <typename T>220 struct A {221 B b;222 };223 template <typename T>224 void foo(A<T> a) {225 auto b = a.b;226 b.waldo;227 }228 )cpp";229 expectResolution(230 Code, &HeuristicResolver::resolveMemberExpr,231 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),232 fieldDecl(hasName("waldo")).bind("output"));233}234 235TEST(HeuristicResolver, MemberExpr_Chained) {236 std::string Code = R"cpp(237 struct A { void foo() {} };238 template <typename T>239 struct B {240 A func(int);241 void bar() {242 func(1).foo();243 }244 };245 )cpp";246 // Test resolution of "foo" in "func(1).foo()".247 expectResolution(248 Code, &HeuristicResolver::resolveMemberExpr,249 cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"),250 cxxMethodDecl(hasName("foo")).bind("output"));251}252 253TEST(HeuristicResolver, MemberExpr_Chained_ReferenceType) {254 std::string Code = R"cpp(255 struct B {256 int waldo;257 };258 template <typename T>259 struct A {260 B &foo();261 };262 template <typename T>263 void bar(A<T> a) {264 a.foo().waldo;265 }266 )cpp";267 // Test resolution of "waldo" in "a.foo().waldo"268 expectResolution(269 Code, &HeuristicResolver::resolveMemberExpr,270 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),271 fieldDecl(hasName("waldo")).bind("output"));272}273 274TEST(HeuristicResolver, MemberExpr_Chained_PointerArrow) {275 std::string Code = R"cpp(276 struct B {277 int waldo;278 };279 template <typename T>280 B* foo(T);281 template <class T>282 void bar(T t) {283 foo(t)->waldo;284 }285 )cpp";286 // Test resolution of "waldo" in "foo(t)->waldo"287 expectResolution(288 Code, &HeuristicResolver::resolveMemberExpr,289 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),290 fieldDecl(hasName("waldo")).bind("output"));291}292 293TEST(HeuristicResolver, MemberExpr_Chained_PointerDeref) {294 std::string Code = R"cpp(295 struct B {296 int waldo;297 };298 template <typename T>299 B* foo(T);300 template <class T>301 void bar(T t) {302 (*foo(t)).waldo;303 }304 )cpp";305 // Test resolution of "waldo" in "foo(t)->waldo"306 expectResolution(307 Code, &HeuristicResolver::resolveMemberExpr,308 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),309 fieldDecl(hasName("waldo")).bind("output"));310}311 312TEST(HeuristicResolver, MemberExpr_Chained_Overload) {313 std::string Code = R"cpp(314 struct B {315 int waldo;316 };317 B overloaded(int);318 B overloaded(double);319 template <typename T>320 void foo(T t) {321 overloaded(t).waldo;322 }323 )cpp";324 // Test resolution of "waldo" in "overloaded(t).waldo"325 expectResolution(326 Code, &HeuristicResolver::resolveMemberExpr,327 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),328 fieldDecl(hasName("waldo")).bind("output"));329}330 331TEST(HeuristicResolver, MemberExpr_CallToFunctionTemplate) {332 std::string Code = R"cpp(333 struct B {334 int waldo;335 };336 template <typename T>337 B bar(T);338 template <typename T>339 void foo(T t) {340 bar(t).waldo;341 }342 )cpp";343 // Test resolution of "waldo" in "bar(t).waldo"344 expectResolution(345 Code, &HeuristicResolver::resolveMemberExpr,346 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),347 fieldDecl(hasName("waldo")).bind("output"));348}349 350TEST(HeuristicResolver, MemberExpr_ReferenceType) {351 std::string Code = R"cpp(352 struct B {353 int waldo;354 };355 template <typename T>356 struct A {357 B &b;358 };359 template <typename T>360 void foo(A<T> &a) {361 a.b.waldo;362 }363 )cpp";364 // Test resolution of "waldo" in "a.b.waldo".365 expectResolution(366 Code, &HeuristicResolver::resolveMemberExpr,367 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),368 fieldDecl(hasName("waldo")).bind("output"));369}370 371TEST(HeuristicResolver, MemberExpr_PointerType) {372 std::string Code = R"cpp(373 struct B {374 int waldo;375 };376 template <typename T>377 struct A {378 B *b;379 };380 template <typename T>381 void foo(A<T> &a) {382 a.b->waldo;383 }384 )cpp";385 // Test resolution of "waldo" in "a.b->waldo".386 expectResolution(387 Code, &HeuristicResolver::resolveMemberExpr,388 cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),389 fieldDecl(hasName("waldo")).bind("output"));390}391 392TEST(HeuristicResolver, MemberExpr_TemplateArgs) {393 std::string Code = R"cpp(394 struct Foo {395 static Foo k(int);396 template <typename T> T convert();397 };398 template <typename T>399 void test() {400 Foo::k(T()).template convert<T>();401 }402 )cpp";403 // Test resolution of "convert" in "Foo::k(T()).template convert<T>()".404 expectResolution(405 Code, &HeuristicResolver::resolveMemberExpr,406 cxxDependentScopeMemberExpr(hasMemberName("convert")).bind("input"),407 functionTemplateDecl(hasName("convert")).bind("output"));408}409 410TEST(HeuristicResolver, MemberExpr_TypeAlias) {411 std::string Code = R"cpp(412 template <typename T>413 struct Waldo {414 void find();415 };416 template <typename T>417 using Wally = Waldo<T>;418 template <typename T>419 void foo(Wally<T> w) {420 w.find();421 }422 )cpp";423 // Test resolution of "find" in "w.find()".424 expectResolution(425 Code, &HeuristicResolver::resolveMemberExpr,426 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),427 cxxMethodDecl(hasName("find")).bind("output"));428}429 430TEST(HeuristicResolver, MemberExpr_BaseClass_TypeAlias) {431 std::string Code = R"cpp(432 template <typename T>433 struct Waldo {434 void find();435 };436 template <typename T>437 using Wally = Waldo<T>;438 template <typename T>439 struct S : Wally<T> {440 void foo() {441 this->find();442 }443 };444 )cpp";445 // Test resolution of "find" in "this->find()".446 expectResolution(447 Code, &HeuristicResolver::resolveMemberExpr,448 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),449 cxxMethodDecl(hasName("find")).bind("output"));450}451 452TEST(HeuristicResolver, MemberExpr_Metafunction) {453 std::string Code = R"cpp(454 template <typename T>455 struct Waldo {456 void find();457 };458 template <typename T>459 struct MetaWaldo {460 using Type = Waldo<T>;461 };462 template <typename T>463 void foo(typename MetaWaldo<T>::Type w) {464 w.find();465 }466 )cpp";467 // Test resolution of "find" in "w.find()".468 expectResolution(469 Code, &HeuristicResolver::resolveMemberExpr,470 cxxDependentScopeMemberExpr(hasMemberName("find")).bind("input"),471 cxxMethodDecl(hasName("find")).bind("output"));472}473 474TEST(HeuristicResolver, MemberExpr_Metafunction_Enumerator) {475 std::string Code = R"cpp(476 enum class State { Hidden };477 template <typename T>478 struct Meta {479 using Type = State;480 };481 template <typename T>482 void foo(typename Meta<T>::Type t) {483 t.Hidden;484 }485 )cpp";486 // Test resolution of "Hidden" in "t.Hidden".487 expectResolution(488 Code, &HeuristicResolver::resolveMemberExpr,489 cxxDependentScopeMemberExpr(hasMemberName("Hidden")).bind("input"),490 enumConstantDecl(hasName("Hidden")).bind("output"));491}492 493TEST(HeuristicResolver, MemberExpr_DeducedNonTypeTemplateParameter) {494 std::string Code = R"cpp(495 template <int N>496 struct Waldo {497 const int found = N;498 };499 template <Waldo W>500 int foo() {501 return W.found;502 }503 )cpp";504 // Test resolution of "found" in "W.found".505 expectResolution(506 Code, &HeuristicResolver::resolveMemberExpr,507 cxxDependentScopeMemberExpr(hasMemberName("found")).bind("input"),508 fieldDecl(hasName("found")).bind("output"));509}510 511TEST(HeuristicResolver, MemberExpr_HangIssue126536) {512 std::string Code = R"cpp(513 template <class T>514 void foo() {515 T bar;516 auto baz = (bar, bar);517 baz.foo();518 }519 )cpp";520 // Test resolution of "foo" in "baz.foo()".521 // Here, we are testing that we do not get into an infinite loop.522 expectResolution(523 Code, &HeuristicResolver::resolveMemberExpr,524 cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"));525}526 527TEST(HeuristicResolver, MemberExpr_HangOnLongCallChain) {528 const size_t CallChainLength = 50;529 std::string Code = R"cpp(530 template <typename T>531 void foo(T t) {532 t533 )cpp";534 for (size_t I = 0; I < CallChainLength; ++I)535 Code.append(".method()\n");536 Code.append(R"cpp(537 .lastMethod();538 }539 )cpp");540 // Test that resolution of a name whose base is a long call chain541 // does not hang. Note that the hang for which this is a regression542 // test is finite (exponential runtime in the length of the chain),543 // so a "failure" here manifests as abnormally long runtime.544 expectResolution(545 Code, &HeuristicResolver::resolveMemberExpr,546 cxxDependentScopeMemberExpr(hasMemberName("lastMethod")).bind("input"));547}548 549TEST(HeuristicResolver, MemberExpr_DefaultTemplateArgument) {550 std::string Code = R"cpp(551 struct Default {552 void foo();553 };554 template <typename T = Default>555 void bar(T t) {556 t.foo();557 }558 )cpp";559 // Test resolution of "foo" in "t.foo()".560 expectResolution(561 Code, &HeuristicResolver::resolveMemberExpr,562 cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"),563 cxxMethodDecl(hasName("foo")).bind("output"));564}565 566TEST(HeuristicResolver, MemberExpr_DefaultTemplateArgument_Recursive) {567 std::string Code = R"cpp(568 struct Default {569 void foo();570 };571 template <typename D = Default, typename T = D>572 void bar(T t) {573 t.foo();574 }575 )cpp";576 // Test resolution of "foo" in "t.foo()".577 expectResolution(578 Code, &HeuristicResolver::resolveMemberExpr,579 cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"),580 cxxMethodDecl(hasName("foo")).bind("output"));581}582 583TEST(HeuristicResolver, MemberExpr_DefaultTemplateTemplateArgument) {584 std::string Code = R"cpp(585 template <typename T>586 struct vector {587 void push_back(T);588 };589 template <typename Element, template <typename> class Container = vector>590 void foo(Container<Element> c, Element e) {591 c.push_back(e);592 }593 )cpp";594 // Test resolution of "push_back" in "c.push_back(e)".595 expectResolution(596 Code, &HeuristicResolver::resolveMemberExpr,597 cxxDependentScopeMemberExpr(hasMemberName("push_back")).bind("input"),598 cxxMethodDecl(hasName("push_back")).bind("output"));599}600 601TEST(HeuristicResolver, MemberExpr_ExplicitObjectParameter) {602 std::string Code = R"cpp(603 struct Foo {604 int m_int;605 606 int bar(this auto&& self) {607 return self.m_int;608 }609 };610 )cpp";611 // Test resolution of "m_int" in "self.m_int()".612 expectResolution(613 Code, &HeuristicResolver::resolveMemberExpr,614 cxxDependentScopeMemberExpr(hasMemberName("m_int")).bind("input"),615 fieldDecl(hasName("m_int")).bind("output"));616}617 618TEST(HeuristicResolver, DeclRefExpr_StaticMethod) {619 std::string Code = R"cpp(620 template <typename T>621 struct S {622 static void bar() {}623 };624 625 template <typename T>626 void foo() {627 S<T>::bar();628 }629 )cpp";630 // Test resolution of "bar" in "S<T>::bar()".631 expectResolution(632 Code, &HeuristicResolver::resolveDeclRefExpr,633 dependentScopeDeclRefExpr(hasDependentName("bar")).bind("input"),634 cxxMethodDecl(hasName("bar")).bind("output"));635}636 637TEST(HeuristicResolver, DeclRefExpr_DefaultTemplateArgument) {638 std::string Code = R"cpp(639 struct Default {640 static void foo();641 };642 template <typename T = Default>643 void bar() {644 T::foo();645 }646 )cpp";647 // Test resolution of "foo" in "T::foo()".648 expectResolution(649 Code, &HeuristicResolver::resolveDeclRefExpr,650 dependentScopeDeclRefExpr(hasDependentName("foo")).bind("input"),651 cxxMethodDecl(hasName("foo")).bind("output"));652}653 654TEST(HeuristicResolver, DeclRefExpr_StaticOverloads) {655 std::string Code = R"cpp(656 template <typename T>657 struct S {658 static void bar(int);659 static void bar(float);660 };661 662 template <typename T, typename U>663 void foo(U u) {664 S<T>::bar(u);665 }666 )cpp";667 // Test resolution of "bar" in "S<T>::bar(u)". Both overloads should be found.668 expectResolution(669 Code, &HeuristicResolver::resolveDeclRefExpr,670 dependentScopeDeclRefExpr(hasDependentName("bar")).bind("input"),671 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("int"))))672 .bind("output"),673 cxxMethodDecl(hasName("bar"), hasParameter(0, hasType(asString("float"))))674 .bind("output"));675}676 677TEST(HeuristicResolver, DeclRefExpr_Enumerator) {678 std::string Code = R"cpp(679 template <typename T>680 struct Foo {681 enum class E { A, B };682 E e = E::A;683 };684 )cpp";685 // Test resolution of "A" in "E::A".686 expectResolution(687 Code, &HeuristicResolver::resolveDeclRefExpr,688 dependentScopeDeclRefExpr(hasDependentName("A")).bind("input"),689 enumConstantDecl(hasName("A")).bind("output"));690}691 692TEST(HeuristicResolver, DeclRefExpr_RespectScope) {693 std::string Code = R"cpp(694 template <typename Info>695 struct PointerIntPair {696 void *getPointer() const { return Info::getPointer(); }697 };698 )cpp";699 // Test resolution of "getPointer" in "Info::getPointer()".700 // Here, we are testing that we do not incorrectly get the enclosing701 // getPointer() function as a result.702 expectResolution(703 Code, &HeuristicResolver::resolveDeclRefExpr,704 dependentScopeDeclRefExpr(hasDependentName("getPointer")).bind("input"));705}706 707TEST(HeuristicResolver, DeclRefExpr_Nested) {708 std::string Code = R"cpp(709 struct S {710 static int Waldo;711 };712 template <typename T>713 struct Meta {714 using Type = S;715 };716 template <typename T>717 void foo() {718 Meta<T>::Type::Waldo;719 }720 )cpp";721 // Test resolution of "Waldo" in "Meta<T>::Type::Waldo".722 expectResolution(723 Code, &HeuristicResolver::resolveDeclRefExpr,724 dependentScopeDeclRefExpr(hasDependentName("Waldo")).bind("input"),725 varDecl(hasName("Waldo")).bind("output"));726}727 728TEST(HeuristicResolver, DependentNameType) {729 std::string Code = R"cpp(730 template <typename>731 struct A {732 struct B {};733 };734 template <typename T>735 void foo(typename A<T>::B);736 )cpp";737 // Tests resolution of "B" in "A<T>::B".738 expectResolution(739 Code, &HeuristicResolver::resolveDependentNameType,740 functionDecl(hasParameter(0, hasType(dependentNameType().bind("input")))),741 classTemplateDecl(742 has(cxxRecordDecl(has(cxxRecordDecl(hasName("B")).bind("output"))))));743}744 745TEST(HeuristicResolver, DependentNameType_Nested) {746 std::string Code = R"cpp(747 template <typename>748 struct A {749 struct B {750 struct C {};751 };752 };753 template <typename T>754 void foo(typename A<T>::B::C);755 )cpp";756 // Tests resolution of "C" in "A<T>::B::C".757 expectResolution(758 Code, &HeuristicResolver::resolveDependentNameType,759 functionDecl(hasParameter(0, hasType(dependentNameType().bind("input")))),760 classTemplateDecl(has(cxxRecordDecl(has(761 cxxRecordDecl(has(cxxRecordDecl(hasName("C")).bind("output"))))))));762}763 764TEST(HeuristicResolver, DependentNameType_Recursion) {765 std::string Code = R"cpp(766 template <int N>767 struct Waldo {768 using Type = typename Waldo<N - 1>::Type::Next;769 };770 )cpp";771 // Test resolution of "Next" in "typename Waldo<N - 1>::Type::Next".772 // Here, we are testing that we do not get into an infinite recursion.773 expectResolution(Code, &HeuristicResolver::resolveDependentNameType,774 typeAliasDecl(hasType(dependentNameType().bind("input"))));775}776 777TEST(HeuristicResolver, DependentNameType_MutualRecursion) {778 std::string Code = R"cpp(779 template <int N>780 struct Odd;781 template <int N>782 struct Even {783 using Type = typename Odd<N - 1>::Type::Next;784 };785 template <int N>786 struct Odd {787 using Type = typename Even<N - 1>::Type::Next;788 };789 )cpp";790 // Test resolution of "Next" in "typename Even<N - 1>::Type::Next".791 // Similar to the above but we have two mutually recursive templates.792 expectResolution(793 Code, &HeuristicResolver::resolveDependentNameType,794 classTemplateDecl(hasName("Odd"),795 has(cxxRecordDecl(has(typeAliasDecl(796 hasType(dependentNameType().bind("input"))))))));797}798 799TEST(HeuristicResolver, NestedNameSpecifier) {800 // Test resolution of "B" in "A<T>::B::C".801 // Unlike the "C", the "B" does not get its own DependentNameTypeLoc node,802 // so the resolution uses the NestedNameSpecifier as input.803 std::string Code = R"cpp(804 template <typename>805 struct A {806 struct B {807 struct C {};808 };809 };810 template <typename T>811 void foo(typename A<T>::B::C);812 )cpp";813 // Adapt the call to resolveNestedNameSpecifierToType() to the interface814 // expected by expectResolution() (returning a vector of decls).815 ResolveFnT<NestedNameSpecifier> ResolveFn =816 [](const HeuristicResolver *H,817 NestedNameSpecifier NNS) -> std::vector<const NamedDecl *> {818 return {H->resolveNestedNameSpecifierToType(NNS)->getAsCXXRecordDecl()};819 };820 expectResolution<NestedNameSpecifier>(821 Code, ResolveFn,822 nestedNameSpecifier(hasPrefix(specifiesType(823 hasDeclaration(classTemplateDecl(hasName("A"))))))824 .bind("input"),825 classTemplateDecl(826 has(cxxRecordDecl(has(cxxRecordDecl(hasName("B")).bind("output"))))));827}828 829TEST(HeuristicResolver, TemplateSpecializationType) {830 std::string Code = R"cpp(831 template <typename>832 struct A {833 template <typename>834 struct B {};835 };836 template <typename T>837 void foo(typename A<T>::template B<int>);838 )cpp";839 // Test resolution of "B" in "A<T>::template B<int>".840 expectResolution(Code, &HeuristicResolver::resolveTemplateSpecializationType,841 functionDecl(hasParameter(0, hasType(type().bind("input")))),842 classTemplateDecl(has(cxxRecordDecl(843 has(classTemplateDecl(hasName("B")).bind("output"))))));844}845 846TEST(HeuristicResolver, DependentCall_NonMember) {847 std::string Code = R"cpp(848 template <typename T>849 void nonmember(T);850 template <typename T>851 void bar(T t) {852 nonmember(t);853 }854 )cpp";855 // Test resolution of "nonmember" in "nonmember(t)".856 expectResolution(Code, &HeuristicResolver::resolveCalleeOfCallExpr,857 callExpr(callee(unresolvedLookupExpr(hasAnyDeclaration(858 functionTemplateDecl(hasName("nonmember"))))))859 .bind("input"),860 functionTemplateDecl(hasName("nonmember")).bind("output"));861}862 863TEST(HeuristicResolver, DependentCall_Member) {864 std::string Code = R"cpp(865 template <typename T>866 struct A {867 void member(T);868 };869 template <typename T>870 void bar(A<T> a, T t) {871 a.member(t);872 }873 )cpp";874 // Test resolution of "member" in "a.member(t)".875 expectResolution(876 Code, &HeuristicResolver::resolveCalleeOfCallExpr,877 callExpr(callee(cxxDependentScopeMemberExpr(hasMemberName("member"))))878 .bind("input"),879 cxxMethodDecl(hasName("member")).bind("output"));880}881 882TEST(HeuristicResolver, DependentCall_StaticMember) {883 std::string Code = R"cpp(884 template <typename T>885 struct A {886 static void static_member(T);887 };888 template <typename T>889 void bar(T t) {890 A<T>::static_member(t);891 }892 )cpp";893 // Test resolution of "static_member" in "A<T>::static_member(t)".894 expectResolution(Code, &HeuristicResolver::resolveCalleeOfCallExpr,895 callExpr(callee(dependentScopeDeclRefExpr(896 hasDependentName("static_member"))))897 .bind("input"),898 cxxMethodDecl(hasName("static_member")).bind("output"));899}900 901TEST(HeuristicResolver, DependentCall_Overload) {902 std::string Code = R"cpp(903 void overload(int);904 void overload(double);905 template <typename T>906 void bar(T t) {907 overload(t);908 }909 )cpp";910 // Test resolution of "overload" in "overload(t)". Both overload should be911 // found.912 expectResolution(Code, &HeuristicResolver::resolveCalleeOfCallExpr,913 callExpr(callee(unresolvedLookupExpr(hasAnyDeclaration(914 functionDecl(hasName("overload"))))))915 .bind("input"),916 functionDecl(hasName("overload"),917 hasParameter(0, hasType(asString("double"))))918 .bind("output"),919 functionDecl(hasName("overload"),920 hasParameter(0, hasType(asString("int"))))921 .bind("output"));922}923 924TEST(HeuristicResolver, UsingValueDecl) {925 std::string Code = R"cpp(926 template <typename T>927 struct Base {928 void waldo();929 };930 template <typename T>931 struct Derived : Base<T> {932 using Base<T>::waldo;933 };934 )cpp";935 // Test resolution of "waldo" in "Base<T>::waldo".936 expectResolution(Code, &HeuristicResolver::resolveUsingValueDecl,937 unresolvedUsingValueDecl(hasName("waldo")).bind("input"),938 cxxMethodDecl(hasName("waldo")).bind("output"));939}940 941// `arg` is a ParamVarDecl*, `Expected` is a string942MATCHER_P(ParamNameMatcher, Expected, "paramNameMatcher") {943 EXPECT_TRUE(arg);944 if (IdentifierInfo *Ident = arg->getDeclName().getAsIdentifierInfo()) {945 return Ident->getName() == Expected;946 }947 return false;948}949 950// Helper function for testing HeuristicResolver::getProtoTypeLoc.951// Takes a matcher that selects a callee expression bound to the ID "input",952// calls getProtoTypeLoc() on it, and checks that the call found a953// FunctionProtoTypeLoc encoding the given parameter names.954template <typename InputMatcher, typename... ParameterNames>955void expectParameterNames(ASTContext &Ctx, const InputMatcher &IM,956 ParameterNames... ExpectedParameterNames) {957 auto InputMatches = match(IM, Ctx);958 ASSERT_EQ(1u, InputMatches.size());959 const auto *Input = InputMatches[0].template getNodeAs<Expr>("input");960 ASSERT_TRUE(Input);961 962 HeuristicResolver H(Ctx);963 auto Loc = H.getFunctionProtoTypeLoc(Input);964 ASSERT_TRUE(Loc);965 EXPECT_THAT(Loc.getParams(),966 ElementsAre(ParamNameMatcher(ExpectedParameterNames)...));967}968 969TEST(HeuristicResolver, ProtoTypeLoc) {970 std::string Code = R"cpp(971 void (*f1)(int param1);972 void (__stdcall *f2)(int param2);973 using f3_t = void(*)(int param3);974 f3_t f3;975 using f4_t = void(__stdcall *)(int param4);976 f4_t f4;977 struct S {978 void (*f5)(int param5);979 using f6_t = void(*)(int param6);980 f6_t f6;981 };982 void bar() {983 f1(42);984 f2(42);985 f3(42);986 f4(42);987 S s;988 s.f5(42);989 s.f6(42);990 }991 )cpp";992 auto TU = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++20"});993 auto &Ctx = TU->getASTContext();994 auto checkFreeFunction = [&](llvm::StringRef FunctionName,995 llvm::StringRef ParamName) {996 expectParameterNames(997 Ctx,998 callExpr(999 callee(implicitCastExpr(hasSourceExpression(declRefExpr(1000 to(namedDecl(hasName(FunctionName))))))1001 .bind("input"))),1002 ParamName);1003 };1004 checkFreeFunction("f1", "param1");1005 checkFreeFunction("f2", "param2");1006 checkFreeFunction("f3", "param3");1007 checkFreeFunction("f4", "param4");1008 auto checkMemberFunction = [&](llvm::StringRef MemberName,1009 llvm::StringRef ParamName) {1010 expectParameterNames(1011 Ctx,1012 callExpr(callee(implicitCastExpr(hasSourceExpression(memberExpr(1013 member(hasName(MemberName)))))1014 .bind("input"))),1015 ParamName);1016 };1017 checkMemberFunction("f5", "param5");1018 checkMemberFunction("f6", "param6");1019}1020 1021} // namespace1022} // namespace clang1023