1 //===- unittests/AST/ASTTraverserTest.h------------------------------------===// 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 9 #include "clang/AST/ASTContext.h" 10 #include "clang/AST/ASTNodeTraverser.h" 11 #include "clang/AST/TextNodeDumper.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include "clang/ASTMatchers/ASTMatchers.h" 14 #include "clang/Tooling/Tooling.h" 15 #include "gmock/gmock.h" 16 #include "gtest/gtest.h" 17 18 using namespace clang::tooling; 19 using namespace clang::ast_matchers; 20 21 namespace clang { 22 23 class NodeTreePrinter : public TextTreeStructure { 24 llvm::raw_ostream &OS; 25 26 public: 27 NodeTreePrinter(llvm::raw_ostream &OS) 28 : TextTreeStructure(OS, /* showColors */ false), OS(OS) {} 29 30 void Visit(const Decl *D) { 31 OS << D->getDeclKindName() << "Decl"; 32 if (auto *ND = dyn_cast<NamedDecl>(D)) { 33 OS << " '" << ND->getDeclName() << "'"; 34 } 35 } 36 37 void Visit(const Stmt *S) { 38 OS << S->getStmtClassName(); 39 if (auto *E = dyn_cast<DeclRefExpr>(S)) { 40 OS << " '" << E->getDecl()->getDeclName() << "'"; 41 } 42 } 43 44 void Visit(QualType QT) { 45 OS << "QualType " << QT.split().Quals.getAsString(); 46 } 47 48 void Visit(const Type *T) { OS << T->getTypeClassName() << "Type"; } 49 50 void Visit(const comments::Comment *C, const comments::FullComment *FC) { 51 OS << C->getCommentKindName(); 52 } 53 54 void Visit(const CXXCtorInitializer *Init) { OS << "CXXCtorInitializer"; } 55 56 void Visit(const Attr *A) { 57 switch (A->getKind()) { 58 #define ATTR(X) \ 59 case attr::X: \ 60 OS << #X; \ 61 break; 62 #include "clang/Basic/AttrList.inc" 63 } 64 OS << "Attr"; 65 } 66 67 void Visit(const OMPClause *C) { OS << "OMPClause"; } 68 void Visit(const TemplateArgument &A, SourceRange R = {}, 69 const Decl *From = nullptr, const char *Label = nullptr) { 70 OS << "TemplateArgument"; 71 } 72 73 template <typename... T> void Visit(T...) {} 74 }; 75 76 class TestASTDumper : public ASTNodeTraverser<TestASTDumper, NodeTreePrinter> { 77 78 NodeTreePrinter MyNodeRecorder; 79 80 public: 81 TestASTDumper(llvm::raw_ostream &OS) : MyNodeRecorder(OS) {} 82 NodeTreePrinter &doGetNodeDelegate() { return MyNodeRecorder; } 83 }; 84 85 template <typename... NodeType> std::string dumpASTString(NodeType &&... N) { 86 std::string Buffer; 87 llvm::raw_string_ostream OS(Buffer); 88 89 TestASTDumper Dumper(OS); 90 91 OS << "\n"; 92 93 Dumper.Visit(std::forward<NodeType &&>(N)...); 94 95 return OS.str(); 96 } 97 98 template <typename... NodeType> 99 std::string dumpASTString(ast_type_traits::TraversalKind TK, NodeType &&... N) { 100 std::string Buffer; 101 llvm::raw_string_ostream OS(Buffer); 102 103 TestASTDumper Dumper(OS); 104 Dumper.SetTraversalKind(TK); 105 106 OS << "\n"; 107 108 Dumper.Visit(std::forward<NodeType &&>(N)...); 109 110 return OS.str(); 111 } 112 113 const FunctionDecl *getFunctionNode(clang::ASTUnit *AST, 114 const std::string &Name) { 115 auto Result = ast_matchers::match(functionDecl(hasName(Name)).bind("fn"), 116 AST->getASTContext()); 117 EXPECT_EQ(Result.size(), 1u); 118 return Result[0].getNodeAs<FunctionDecl>("fn"); 119 } 120 121 template <typename T> struct Verifier { 122 static void withDynNode(T Node, const std::string &DumpString) { 123 EXPECT_EQ(dumpASTString(ast_type_traits::DynTypedNode::create(Node)), 124 DumpString); 125 } 126 }; 127 128 template <typename T> struct Verifier<T *> { 129 static void withDynNode(T *Node, const std::string &DumpString) { 130 EXPECT_EQ(dumpASTString(ast_type_traits::DynTypedNode::create(*Node)), 131 DumpString); 132 } 133 }; 134 135 template <typename T> 136 void verifyWithDynNode(T Node, const std::string &DumpString) { 137 EXPECT_EQ(dumpASTString(Node), DumpString); 138 139 Verifier<T>::withDynNode(Node, DumpString); 140 } 141 142 TEST(Traverse, Dump) { 143 144 auto AST = buildASTFromCode(R"cpp( 145 struct A { 146 int m_number; 147 148 /// CTor 149 A() : m_number(42) {} 150 151 [[nodiscard]] const int func() { 152 return 42; 153 } 154 155 }; 156 157 template<typename T> 158 struct templ 159 { 160 }; 161 162 template<> 163 struct templ<int> 164 { 165 }; 166 167 void parmvardecl_attr(struct A __attribute__((address_space(19)))*); 168 169 )cpp"); 170 171 const FunctionDecl *Func = getFunctionNode(AST.get(), "func"); 172 173 verifyWithDynNode(Func, 174 R"cpp( 175 CXXMethodDecl 'func' 176 |-CompoundStmt 177 | `-ReturnStmt 178 | `-IntegerLiteral 179 `-WarnUnusedResultAttr 180 )cpp"); 181 182 Stmt *Body = Func->getBody(); 183 184 verifyWithDynNode(Body, 185 R"cpp( 186 CompoundStmt 187 `-ReturnStmt 188 `-IntegerLiteral 189 )cpp"); 190 191 QualType QT = Func->getType(); 192 193 verifyWithDynNode(QT, 194 R"cpp( 195 FunctionProtoType 196 `-QualType const 197 `-BuiltinType 198 )cpp"); 199 200 const FunctionDecl *CTorFunc = getFunctionNode(AST.get(), "A"); 201 202 verifyWithDynNode(CTorFunc->getType(), 203 R"cpp( 204 FunctionProtoType 205 `-BuiltinType 206 )cpp"); 207 208 Attr *A = *Func->attr_begin(); 209 210 { 211 std::string expectedString = R"cpp( 212 WarnUnusedResultAttr 213 )cpp"; 214 215 EXPECT_EQ(dumpASTString(A), expectedString); 216 } 217 218 auto *CTor = dyn_cast<CXXConstructorDecl>(CTorFunc); 219 const CXXCtorInitializer *Init = *CTor->init_begin(); 220 221 verifyWithDynNode(Init, 222 R"cpp( 223 CXXCtorInitializer 224 `-IntegerLiteral 225 )cpp"); 226 227 const comments::FullComment *Comment = 228 AST->getASTContext().getLocalCommentForDeclUncached(CTorFunc); 229 { 230 std::string expectedString = R"cpp( 231 FullComment 232 `-ParagraphComment 233 `-TextComment 234 )cpp"; 235 EXPECT_EQ(dumpASTString(Comment, Comment), expectedString); 236 } 237 238 auto Result = ast_matchers::match( 239 classTemplateSpecializationDecl(hasName("templ")).bind("fn"), 240 AST->getASTContext()); 241 EXPECT_EQ(Result.size(), 1u); 242 auto Templ = Result[0].getNodeAs<ClassTemplateSpecializationDecl>("fn"); 243 244 TemplateArgument TA = Templ->getTemplateArgs()[0]; 245 246 verifyWithDynNode(TA, 247 R"cpp( 248 TemplateArgument 249 )cpp"); 250 251 Func = getFunctionNode(AST.get(), "parmvardecl_attr"); 252 253 const auto *Parm = Func->getParamDecl(0); 254 const auto TL = Parm->getTypeSourceInfo()->getTypeLoc(); 255 ASSERT_TRUE(TL.getType()->isPointerType()); 256 257 const auto ATL = TL.getNextTypeLoc().getAs<AttributedTypeLoc>(); 258 const auto *AS = cast<AddressSpaceAttr>(ATL.getAttr()); 259 EXPECT_EQ(toTargetAddressSpace(static_cast<LangAS>(AS->getAddressSpace())), 260 19u); 261 } 262 263 TEST(Traverse, IgnoreUnlessSpelledInSourceStructs) { 264 auto AST = buildASTFromCode(R"cpp( 265 266 struct MyStruct { 267 MyStruct(); 268 MyStruct(int i) { 269 MyStruct(); 270 } 271 ~MyStruct(); 272 }; 273 274 )cpp"); 275 276 auto BN = ast_matchers::match( 277 cxxConstructorDecl(hasName("MyStruct"), 278 hasParameter(0, parmVarDecl(hasType(isInteger())))) 279 .bind("ctor"), 280 AST->getASTContext()); 281 EXPECT_EQ(BN.size(), 1u); 282 283 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 284 BN[0].getNodeAs<Decl>("ctor")), 285 R"cpp( 286 CXXConstructorDecl 'MyStruct' 287 |-ParmVarDecl 'i' 288 `-CompoundStmt 289 `-CXXTemporaryObjectExpr 290 )cpp"); 291 292 EXPECT_EQ( 293 dumpASTString(ast_type_traits::TK_AsIs, BN[0].getNodeAs<Decl>("ctor")), 294 R"cpp( 295 CXXConstructorDecl 'MyStruct' 296 |-ParmVarDecl 'i' 297 `-CompoundStmt 298 `-ExprWithCleanups 299 `-CXXBindTemporaryExpr 300 `-CXXTemporaryObjectExpr 301 )cpp"); 302 } 303 304 TEST(Traverse, IgnoreUnlessSpelledInSourceReturnStruct) { 305 306 auto AST = buildASTFromCode(R"cpp( 307 struct Retval { 308 Retval() {} 309 ~Retval() {} 310 }; 311 312 Retval someFun(); 313 314 void foo() 315 { 316 someFun(); 317 } 318 )cpp"); 319 320 auto BN = ast_matchers::match(functionDecl(hasName("foo")).bind("fn"), 321 AST->getASTContext()); 322 EXPECT_EQ(BN.size(), 1u); 323 324 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 325 BN[0].getNodeAs<Decl>("fn")), 326 R"cpp( 327 FunctionDecl 'foo' 328 `-CompoundStmt 329 `-CallExpr 330 `-DeclRefExpr 'someFun' 331 )cpp"); 332 333 EXPECT_EQ( 334 dumpASTString(ast_type_traits::TK_AsIs, BN[0].getNodeAs<Decl>("fn")), 335 R"cpp( 336 FunctionDecl 'foo' 337 `-CompoundStmt 338 `-ExprWithCleanups 339 `-CXXBindTemporaryExpr 340 `-CallExpr 341 `-ImplicitCastExpr 342 `-DeclRefExpr 'someFun' 343 )cpp"); 344 } 345 346 TEST(Traverse, IgnoreUnlessSpelledInSourceReturns) { 347 348 auto AST = buildASTFromCode(R"cpp( 349 350 struct A 351 { 352 }; 353 354 struct B 355 { 356 B(int); 357 B(A const& a); 358 B(); 359 }; 360 361 struct C 362 { 363 operator B(); 364 }; 365 366 B func1() { 367 return 42; 368 } 369 370 B func2() { 371 return B{42}; 372 } 373 374 B func3() { 375 return B(42); 376 } 377 378 B func4() { 379 return B(); 380 } 381 382 B func5() { 383 return B{}; 384 } 385 386 B func6() { 387 return C(); 388 } 389 390 B func7() { 391 return A(); 392 } 393 394 B func8() { 395 return C{}; 396 } 397 398 B func9() { 399 return A{}; 400 } 401 402 B func10() { 403 A a; 404 return a; 405 } 406 407 B func11() { 408 B b; 409 return b; 410 } 411 412 B func12() { 413 C c; 414 return c; 415 } 416 417 )cpp"); 418 419 auto getFunctionNode = [&AST](const std::string &name) { 420 auto BN = ast_matchers::match(functionDecl(hasName(name)).bind("fn"), 421 AST->getASTContext()); 422 EXPECT_EQ(BN.size(), 1u); 423 return BN[0].getNodeAs<Decl>("fn"); 424 }; 425 426 { 427 auto FN = getFunctionNode("func1"); 428 llvm::StringRef Expected = R"cpp( 429 FunctionDecl 'func1' 430 `-CompoundStmt 431 `-ReturnStmt 432 `-ExprWithCleanups 433 `-CXXConstructExpr 434 `-MaterializeTemporaryExpr 435 `-ImplicitCastExpr 436 `-CXXConstructExpr 437 `-IntegerLiteral 438 )cpp"; 439 440 EXPECT_EQ(dumpASTString(ast_type_traits::TK_AsIs, FN), Expected); 441 442 Expected = R"cpp( 443 FunctionDecl 'func1' 444 `-CompoundStmt 445 `-ReturnStmt 446 `-IntegerLiteral 447 )cpp"; 448 EXPECT_EQ( 449 dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, FN), 450 Expected); 451 } 452 453 llvm::StringRef Expected = R"cpp( 454 FunctionDecl 'func2' 455 `-CompoundStmt 456 `-ReturnStmt 457 `-CXXTemporaryObjectExpr 458 `-IntegerLiteral 459 )cpp"; 460 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 461 getFunctionNode("func2")), 462 Expected); 463 464 Expected = R"cpp( 465 FunctionDecl 'func3' 466 `-CompoundStmt 467 `-ReturnStmt 468 `-CXXFunctionalCastExpr 469 `-IntegerLiteral 470 )cpp"; 471 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 472 getFunctionNode("func3")), 473 Expected); 474 475 Expected = R"cpp( 476 FunctionDecl 'func4' 477 `-CompoundStmt 478 `-ReturnStmt 479 `-CXXTemporaryObjectExpr 480 )cpp"; 481 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 482 getFunctionNode("func4")), 483 Expected); 484 485 Expected = R"cpp( 486 FunctionDecl 'func5' 487 `-CompoundStmt 488 `-ReturnStmt 489 `-CXXTemporaryObjectExpr 490 )cpp"; 491 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 492 getFunctionNode("func5")), 493 Expected); 494 495 Expected = R"cpp( 496 FunctionDecl 'func6' 497 `-CompoundStmt 498 `-ReturnStmt 499 `-CXXTemporaryObjectExpr 500 )cpp"; 501 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 502 getFunctionNode("func6")), 503 Expected); 504 505 Expected = R"cpp( 506 FunctionDecl 'func7' 507 `-CompoundStmt 508 `-ReturnStmt 509 `-CXXTemporaryObjectExpr 510 )cpp"; 511 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 512 getFunctionNode("func7")), 513 Expected); 514 515 Expected = R"cpp( 516 FunctionDecl 'func8' 517 `-CompoundStmt 518 `-ReturnStmt 519 `-CXXFunctionalCastExpr 520 `-InitListExpr 521 )cpp"; 522 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 523 getFunctionNode("func8")), 524 Expected); 525 526 Expected = R"cpp( 527 FunctionDecl 'func9' 528 `-CompoundStmt 529 `-ReturnStmt 530 `-CXXFunctionalCastExpr 531 `-InitListExpr 532 )cpp"; 533 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 534 getFunctionNode("func9")), 535 Expected); 536 537 Expected = R"cpp( 538 FunctionDecl 'func10' 539 `-CompoundStmt 540 |-DeclStmt 541 | `-VarDecl 'a' 542 | `-CXXConstructExpr 543 `-ReturnStmt 544 `-DeclRefExpr 'a' 545 )cpp"; 546 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 547 getFunctionNode("func10")), 548 Expected); 549 550 Expected = R"cpp( 551 FunctionDecl 'func11' 552 `-CompoundStmt 553 |-DeclStmt 554 | `-VarDecl 'b' 555 | `-CXXConstructExpr 556 `-ReturnStmt 557 `-DeclRefExpr 'b' 558 )cpp"; 559 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 560 getFunctionNode("func11")), 561 Expected); 562 563 Expected = R"cpp( 564 FunctionDecl 'func12' 565 `-CompoundStmt 566 |-DeclStmt 567 | `-VarDecl 'c' 568 | `-CXXConstructExpr 569 `-ReturnStmt 570 `-DeclRefExpr 'c' 571 )cpp"; 572 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, 573 getFunctionNode("func12")), 574 Expected); 575 } 576 577 TEST(Traverse, LambdaUnlessSpelledInSource) { 578 579 auto AST = 580 buildASTFromCodeWithArgs(R"cpp( 581 582 void captures() { 583 int a = 0; 584 int b = 0; 585 int d = 0; 586 int f = 0; 587 588 [a, &b, c = d, &e = f](int g, int h = 42) {}; 589 } 590 591 void templated() { 592 int a = 0; 593 [a]<typename T>(T t) {}; 594 } 595 596 struct SomeStruct { 597 int a = 0; 598 void capture_this() { 599 [this]() {}; 600 } 601 void capture_this_copy() { 602 [self = *this]() {}; 603 } 604 }; 605 )cpp", 606 {"-Wno-unused-value", "-Wno-c++2a-extensions"}); 607 608 auto getLambdaNode = [&AST](const std::string &name) { 609 auto BN = ast_matchers::match( 610 lambdaExpr(hasAncestor(functionDecl(hasName(name)))).bind("lambda"), 611 AST->getASTContext()); 612 EXPECT_EQ(BN.size(), 1u); 613 return BN[0].getNodeAs<LambdaExpr>("lambda"); 614 }; 615 616 { 617 auto L = getLambdaNode("captures"); 618 619 llvm::StringRef Expected = R"cpp( 620 LambdaExpr 621 |-DeclRefExpr 'a' 622 |-DeclRefExpr 'b' 623 |-VarDecl 'c' 624 | `-DeclRefExpr 'd' 625 |-VarDecl 'e' 626 | `-DeclRefExpr 'f' 627 |-ParmVarDecl 'g' 628 |-ParmVarDecl 'h' 629 | `-IntegerLiteral 630 `-CompoundStmt 631 )cpp"; 632 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L), 633 Expected); 634 635 Expected = R"cpp( 636 LambdaExpr 637 |-CXXRecordDecl '' 638 | |-CXXMethodDecl 'operator()' 639 | | |-ParmVarDecl 'g' 640 | | |-ParmVarDecl 'h' 641 | | | `-IntegerLiteral 642 | | `-CompoundStmt 643 | |-FieldDecl '' 644 | |-FieldDecl '' 645 | |-FieldDecl '' 646 | |-FieldDecl '' 647 | `-CXXDestructorDecl '~' 648 |-ImplicitCastExpr 649 | `-DeclRefExpr 'a' 650 |-DeclRefExpr 'b' 651 |-ImplicitCastExpr 652 | `-DeclRefExpr 'd' 653 |-DeclRefExpr 'f' 654 `-CompoundStmt 655 )cpp"; 656 EXPECT_EQ(dumpASTString(ast_type_traits::TK_AsIs, L), Expected); 657 } 658 659 { 660 auto L = getLambdaNode("templated"); 661 662 llvm::StringRef Expected = R"cpp( 663 LambdaExpr 664 |-DeclRefExpr 'a' 665 |-TemplateTypeParmDecl 'T' 666 |-ParmVarDecl 't' 667 `-CompoundStmt 668 )cpp"; 669 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L), 670 Expected); 671 } 672 673 { 674 auto L = getLambdaNode("capture_this"); 675 676 llvm::StringRef Expected = R"cpp( 677 LambdaExpr 678 |-CXXThisExpr 679 `-CompoundStmt 680 )cpp"; 681 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L), 682 Expected); 683 } 684 685 { 686 auto L = getLambdaNode("capture_this_copy"); 687 688 llvm::StringRef Expected = R"cpp( 689 LambdaExpr 690 |-VarDecl 'self' 691 | `-UnaryOperator 692 | `-CXXThisExpr 693 `-CompoundStmt 694 )cpp"; 695 EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L), 696 Expected); 697 } 698 } 699 700 } // namespace clang 701