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