1 //===- unittests/AST/DeclTest.cpp --- Declaration tests -------------------===// 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 // Unit tests for Decl nodes in the AST. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/AST/Decl.h" 14 #include "clang/AST/ASTContext.h" 15 #include "clang/AST/DeclTemplate.h" 16 #include "clang/AST/Mangle.h" 17 #include "clang/ASTMatchers/ASTMatchFinder.h" 18 #include "clang/ASTMatchers/ASTMatchers.h" 19 #include "clang/Basic/Diagnostic.h" 20 #include "clang/Basic/LLVM.h" 21 #include "clang/Basic/TargetInfo.h" 22 #include "clang/Lex/Lexer.h" 23 #include "clang/Tooling/Tooling.h" 24 #include "llvm/IR/DataLayout.h" 25 #include "llvm/Testing/Annotations/Annotations.h" 26 #include "gtest/gtest.h" 27 28 using namespace clang::ast_matchers; 29 using namespace clang::tooling; 30 using namespace clang; 31 32 TEST(Decl, CleansUpAPValues) { 33 MatchFinder Finder; 34 std::unique_ptr<FrontendActionFactory> Factory( 35 newFrontendActionFactory(&Finder)); 36 37 // This is a regression test for a memory leak in APValues for structs that 38 // allocate memory. This test only fails if run under valgrind with full leak 39 // checking enabled. 40 std::vector<std::string> Args(1, "-std=c++11"); 41 Args.push_back("-fno-ms-extensions"); 42 ASSERT_TRUE(runToolOnCodeWithArgs( 43 Factory->create(), 44 "struct X { int a; }; constexpr X x = { 42 };" 45 "union Y { constexpr Y(int a) : a(a) {} int a; }; constexpr Y y = { 42 };" 46 "constexpr int z[2] = { 42, 43 };" 47 "constexpr int __attribute__((vector_size(16))) v1 = {};" 48 "\n#ifdef __SIZEOF_INT128__\n" 49 "constexpr __uint128_t large_int = 0xffffffffffffffff;" 50 "constexpr __uint128_t small_int = 1;" 51 "\n#endif\n" 52 "constexpr double d1 = 42.42;" 53 "constexpr long double d2 = 42.42;" 54 "constexpr _Complex long double c1 = 42.0i;" 55 "constexpr _Complex long double c2 = 42.0;" 56 "template<int N> struct A : A<N-1> {};" 57 "template<> struct A<0> { int n; }; A<50> a;" 58 "constexpr int &r = a.n;" 59 "constexpr int A<50>::*p = &A<50>::n;" 60 "void f() { foo: bar: constexpr int k = __builtin_constant_p(0) ?" 61 " (char*)&&foo - (char*)&&bar : 0; }", 62 Args)); 63 64 // FIXME: Once this test starts breaking we can test APValue::needsCleanup 65 // for ComplexInt. 66 ASSERT_FALSE(runToolOnCodeWithArgs( 67 Factory->create(), 68 "constexpr _Complex __uint128_t c = 0xffffffffffffffff;", 69 Args)); 70 } 71 72 TEST(Decl, AsmLabelAttr) { 73 // Create two method decls: `f` and `g`. 74 StringRef Code = R"( 75 struct S { 76 void f() {} 77 void g() {} 78 }; 79 )"; 80 auto AST = 81 tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"}); 82 ASTContext &Ctx = AST->getASTContext(); 83 assert(Ctx.getTargetInfo().getUserLabelPrefix() == StringRef("_") && 84 "Expected target to have a global prefix"); 85 DiagnosticsEngine &Diags = AST->getDiagnostics(); 86 87 const auto *DeclS = 88 selectFirst<CXXRecordDecl>("d", match(cxxRecordDecl().bind("d"), Ctx)); 89 NamedDecl *DeclF = *DeclS->method_begin(); 90 NamedDecl *DeclG = *(++DeclS->method_begin()); 91 92 // Attach asm labels to the decls: one literal, and one not. 93 DeclF->addAttr(AsmLabelAttr::Create(Ctx, "foo", /*LiteralLabel=*/true)); 94 DeclG->addAttr(AsmLabelAttr::Create(Ctx, "goo", /*LiteralLabel=*/false)); 95 96 // Mangle the decl names. 97 std::string MangleF, MangleG; 98 std::unique_ptr<ItaniumMangleContext> MC( 99 ItaniumMangleContext::create(Ctx, Diags)); 100 { 101 llvm::raw_string_ostream OS_F(MangleF); 102 llvm::raw_string_ostream OS_G(MangleG); 103 MC->mangleName(DeclF, OS_F); 104 MC->mangleName(DeclG, OS_G); 105 } 106 107 ASSERT_TRUE(0 == MangleF.compare("\x01" "foo")); 108 ASSERT_TRUE(0 == MangleG.compare("goo")); 109 } 110 111 TEST(Decl, MangleDependentSizedArray) { 112 StringRef Code = R"( 113 template <int ...N> 114 int A[] = {N...}; 115 116 template <typename T, int N> 117 struct S { 118 T B[N]; 119 }; 120 )"; 121 auto AST = 122 tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"}); 123 ASTContext &Ctx = AST->getASTContext(); 124 assert(Ctx.getTargetInfo().getUserLabelPrefix() == StringRef("_") && 125 "Expected target to have a global prefix"); 126 DiagnosticsEngine &Diags = AST->getDiagnostics(); 127 128 const auto *DeclA = 129 selectFirst<VarDecl>("A", match(varDecl().bind("A"), Ctx)); 130 const auto *DeclB = 131 selectFirst<FieldDecl>("B", match(fieldDecl().bind("B"), Ctx)); 132 133 std::string MangleA, MangleB; 134 llvm::raw_string_ostream OS_A(MangleA), OS_B(MangleB); 135 std::unique_ptr<ItaniumMangleContext> MC( 136 ItaniumMangleContext::create(Ctx, Diags)); 137 138 MC->mangleCanonicalTypeName(DeclA->getType(), OS_A); 139 MC->mangleCanonicalTypeName(DeclB->getType(), OS_B); 140 141 ASSERT_TRUE(0 == MangleA.compare("_ZTSA_i")); 142 ASSERT_TRUE(0 == MangleB.compare("_ZTSAT0__T_")); 143 } 144 145 TEST(Decl, ConceptDecl) { 146 llvm::StringRef Code(R"( 147 template<class T> 148 concept integral = __is_integral(T); 149 )"); 150 151 auto AST = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++20"}); 152 ASTContext &Ctx = AST->getASTContext(); 153 154 const auto *Decl = 155 selectFirst<ConceptDecl>("decl", match(conceptDecl().bind("decl"), Ctx)); 156 ASSERT_TRUE(Decl != nullptr); 157 EXPECT_EQ(Decl->getName(), "integral"); 158 } 159 160 TEST(Decl, EnumDeclRange) { 161 llvm::Annotations Code(R"( 162 typedef int Foo; 163 [[enum Bar : Foo]];)"); 164 auto AST = tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{}); 165 ASTContext &Ctx = AST->getASTContext(); 166 const auto &SM = Ctx.getSourceManager(); 167 168 const auto *Bar = 169 selectFirst<TagDecl>("Bar", match(enumDecl().bind("Bar"), Ctx)); 170 auto BarRange = 171 Lexer::getAsCharRange(Bar->getSourceRange(), SM, Ctx.getLangOpts()); 172 EXPECT_EQ(SM.getFileOffset(BarRange.getBegin()), Code.range().Begin); 173 EXPECT_EQ(SM.getFileOffset(BarRange.getEnd()), Code.range().End); 174 } 175 176 TEST(Decl, IsInExportDeclContext) { 177 llvm::Annotations Code(R"( 178 export module m; 179 export template <class T> 180 void f() {})"); 181 auto AST = 182 tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); 183 ASTContext &Ctx = AST->getASTContext(); 184 185 const auto *f = 186 selectFirst<FunctionDecl>("f", match(functionDecl().bind("f"), Ctx)); 187 EXPECT_TRUE(f->isInExportDeclContext()); 188 } 189 190 TEST(Decl, InConsistLinkageForTemplates) { 191 llvm::Annotations Code(R"( 192 export module m; 193 export template <class T> 194 void f() {} 195 196 template <> 197 void f<int>() {} 198 199 export template <class T> 200 class C {}; 201 202 template<> 203 class C<int> {}; 204 )"); 205 206 auto AST = 207 tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); 208 ASTContext &Ctx = AST->getASTContext(); 209 210 llvm::SmallVector<ast_matchers::BoundNodes, 2> Funcs = 211 match(functionDecl().bind("f"), Ctx); 212 213 EXPECT_EQ(Funcs.size(), 2U); 214 const FunctionDecl *TemplateF = Funcs[0].getNodeAs<FunctionDecl>("f"); 215 const FunctionDecl *SpecializedF = Funcs[1].getNodeAs<FunctionDecl>("f"); 216 EXPECT_EQ(TemplateF->getLinkageInternal(), 217 SpecializedF->getLinkageInternal()); 218 219 llvm::SmallVector<ast_matchers::BoundNodes, 1> ClassTemplates = 220 match(classTemplateDecl().bind("C"), Ctx); 221 llvm::SmallVector<ast_matchers::BoundNodes, 1> ClassSpecializations = 222 match(classTemplateSpecializationDecl().bind("C"), Ctx); 223 224 EXPECT_EQ(ClassTemplates.size(), 1U); 225 EXPECT_EQ(ClassSpecializations.size(), 1U); 226 const NamedDecl *TemplatedC = ClassTemplates[0].getNodeAs<NamedDecl>("C"); 227 const NamedDecl *SpecializedC = ClassSpecializations[0].getNodeAs<NamedDecl>("C"); 228 EXPECT_EQ(TemplatedC->getLinkageInternal(), 229 SpecializedC->getLinkageInternal()); 230 } 231 232 TEST(Decl, ModuleAndInternalLinkage) { 233 llvm::Annotations Code(R"( 234 export module M; 235 static int a; 236 static int f(int x); 237 238 int b; 239 int g(int x);)"); 240 241 auto AST = 242 tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); 243 ASTContext &Ctx = AST->getASTContext(); 244 245 const auto *a = 246 selectFirst<VarDecl>("a", match(varDecl(hasName("a")).bind("a"), Ctx)); 247 const auto *f = selectFirst<FunctionDecl>( 248 "f", match(functionDecl(hasName("f")).bind("f"), Ctx)); 249 250 EXPECT_EQ(a->getFormalLinkage(), Linkage::Internal); 251 EXPECT_EQ(f->getFormalLinkage(), Linkage::Internal); 252 253 const auto *b = 254 selectFirst<VarDecl>("b", match(varDecl(hasName("b")).bind("b"), Ctx)); 255 const auto *g = selectFirst<FunctionDecl>( 256 "g", match(functionDecl(hasName("g")).bind("g"), Ctx)); 257 258 EXPECT_EQ(b->getFormalLinkage(), Linkage::Module); 259 EXPECT_EQ(g->getFormalLinkage(), Linkage::Module); 260 } 261 262 TEST(Decl, GetNonTransparentDeclContext) { 263 llvm::Annotations Code(R"( 264 export module m3; 265 export template <class> struct X { 266 template <class Self> friend void f(Self &&self) { 267 (Self&)self; 268 } 269 };)"); 270 271 auto AST = 272 tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); 273 ASTContext &Ctx = AST->getASTContext(); 274 275 auto *f = selectFirst<FunctionDecl>( 276 "f", match(functionDecl(hasName("f")).bind("f"), Ctx)); 277 278 EXPECT_TRUE(f->getNonTransparentDeclContext()->isFileContext()); 279 } 280 281 TEST(Decl, MemberFunctionInModules) { 282 llvm::Annotations Code(R"( 283 module; 284 class G { 285 void bar() {} 286 }; 287 export module M; 288 class A { 289 void foo() {} 290 }; 291 )"); 292 293 auto AST = 294 tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); 295 ASTContext &Ctx = AST->getASTContext(); 296 297 auto *foo = selectFirst<FunctionDecl>( 298 "foo", match(functionDecl(hasName("foo")).bind("foo"), Ctx)); 299 300 // The function defined within a class definition is not implicitly inline 301 // if it is not attached to global module 302 EXPECT_FALSE(foo->isInlined()); 303 304 auto *bar = selectFirst<FunctionDecl>( 305 "bar", match(functionDecl(hasName("bar")).bind("bar"), Ctx)); 306 307 // In global module, the function defined within a class definition is 308 // implicitly inline. 309 EXPECT_TRUE(bar->isInlined()); 310 } 311 312 TEST(Decl, MemberFunctionInHeaderUnit) { 313 llvm::Annotations Code(R"( 314 class foo { 315 public: 316 int memFn() { 317 return 43; 318 } 319 }; 320 )"); 321 322 auto AST = tooling::buildASTFromCodeWithArgs( 323 Code.code(), {"-std=c++20", " -xc++-user-header ", "-emit-header-unit"}); 324 ASTContext &Ctx = AST->getASTContext(); 325 326 auto *memFn = selectFirst<FunctionDecl>( 327 "memFn", match(functionDecl(hasName("memFn")).bind("memFn"), Ctx)); 328 329 EXPECT_TRUE(memFn->isInlined()); 330 } 331 332 TEST(Decl, FriendFunctionWithinClassInHeaderUnit) { 333 llvm::Annotations Code(R"( 334 class foo { 335 int value; 336 public: 337 foo(int v) : value(v) {} 338 339 friend int getFooValue(foo f) { 340 return f.value; 341 } 342 }; 343 )"); 344 345 auto AST = tooling::buildASTFromCodeWithArgs( 346 Code.code(), {"-std=c++20", " -xc++-user-header ", "-emit-header-unit"}); 347 ASTContext &Ctx = AST->getASTContext(); 348 349 auto *getFooValue = selectFirst<FunctionDecl>( 350 "getFooValue", 351 match(functionDecl(hasName("getFooValue")).bind("getFooValue"), Ctx)); 352 353 EXPECT_TRUE(getFooValue->isInlined()); 354 } 355 356 TEST(Decl, FunctionDeclBitsShouldNotOverlapWithCXXConstructorDeclBits) { 357 llvm::Annotations Code(R"( 358 struct A { 359 A() : m() {} 360 int m; 361 }; 362 363 A f() { return A(); } 364 )"); 365 366 auto AST = tooling::buildASTFromCodeWithArgs(Code.code(), {"-std=c++14"}); 367 ASTContext &Ctx = AST->getASTContext(); 368 369 auto HasCtorInit = 370 hasAnyConstructorInitializer(cxxCtorInitializer(isMemberInitializer())); 371 auto ImpMoveCtor = 372 cxxConstructorDecl(isMoveConstructor(), isImplicit(), HasCtorInit) 373 .bind("MoveCtor"); 374 375 auto *ToImpMoveCtor = 376 selectFirst<CXXConstructorDecl>("MoveCtor", match(ImpMoveCtor, Ctx)); 377 378 EXPECT_TRUE(ToImpMoveCtor->getNumCtorInitializers() == 1); 379 EXPECT_FALSE(ToImpMoveCtor->FriendConstraintRefersToEnclosingTemplate()); 380 } 381 382 TEST(Decl, NoProtoFunctionDeclAttributes) { 383 llvm::Annotations Code(R"( 384 void f(); 385 )"); 386 387 auto AST = tooling::buildASTFromCodeWithArgs( 388 Code.code(), 389 /*Args=*/{"-target", "i386-apple-darwin", "-x", "objective-c", 390 "-std=c89"}); 391 ASTContext &Ctx = AST->getASTContext(); 392 393 auto *f = selectFirst<FunctionDecl>( 394 "f", match(functionDecl(hasName("f")).bind("f"), Ctx)); 395 396 const auto *FPT = f->getType()->getAs<FunctionNoProtoType>(); 397 398 // Functions without prototypes always have 0 initialized qualifiers 399 EXPECT_FALSE(FPT->isConst()); 400 EXPECT_FALSE(FPT->isVolatile()); 401 EXPECT_FALSE(FPT->isRestrict()); 402 } 403 404 TEST(Decl, ImplicitlyDeclaredAllocationFunctionsInModules) { 405 // C++ [basic.stc.dynamic.general]p2: 406 // The library provides default definitions for the global allocation 407 // and deallocation functions. Some global allocation and deallocation 408 // functions are replaceable ([new.delete]); these are attached to the 409 // global module ([module.unit]). 410 411 llvm::Annotations Code(R"( 412 export module base; 413 414 export struct Base { 415 virtual void hello() = 0; 416 virtual ~Base() = default; 417 }; 418 )"); 419 420 auto AST = 421 tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); 422 ASTContext &Ctx = AST->getASTContext(); 423 424 // void* operator new(std::size_t); 425 auto *SizedOperatorNew = selectFirst<FunctionDecl>( 426 "operator new", 427 match(functionDecl(hasName("operator new"), parameterCountIs(1), 428 hasParameter(0, hasType(isUnsignedInteger()))) 429 .bind("operator new"), 430 Ctx)); 431 ASSERT_TRUE(SizedOperatorNew->getOwningModule()); 432 EXPECT_TRUE(SizedOperatorNew->isFromExplicitGlobalModule()); 433 434 // void* operator new(std::size_t, std::align_val_t); 435 auto *SizedAlignedOperatorNew = selectFirst<FunctionDecl>( 436 "operator new", 437 match(functionDecl( 438 hasName("operator new"), parameterCountIs(2), 439 hasParameter(0, hasType(isUnsignedInteger())), 440 hasParameter(1, hasType(enumDecl(hasName("std::align_val_t"))))) 441 .bind("operator new"), 442 Ctx)); 443 ASSERT_TRUE(SizedAlignedOperatorNew->getOwningModule()); 444 EXPECT_TRUE(SizedAlignedOperatorNew->isFromExplicitGlobalModule()); 445 446 // void* operator new[](std::size_t); 447 auto *SizedArrayOperatorNew = selectFirst<FunctionDecl>( 448 "operator new[]", 449 match(functionDecl(hasName("operator new[]"), parameterCountIs(1), 450 hasParameter(0, hasType(isUnsignedInteger()))) 451 .bind("operator new[]"), 452 Ctx)); 453 ASSERT_TRUE(SizedArrayOperatorNew->getOwningModule()); 454 EXPECT_TRUE(SizedArrayOperatorNew->isFromExplicitGlobalModule()); 455 456 // void* operator new[](std::size_t, std::align_val_t); 457 auto *SizedAlignedArrayOperatorNew = selectFirst<FunctionDecl>( 458 "operator new[]", 459 match(functionDecl( 460 hasName("operator new[]"), parameterCountIs(2), 461 hasParameter(0, hasType(isUnsignedInteger())), 462 hasParameter(1, hasType(enumDecl(hasName("std::align_val_t"))))) 463 .bind("operator new[]"), 464 Ctx)); 465 ASSERT_TRUE(SizedAlignedArrayOperatorNew->getOwningModule()); 466 EXPECT_TRUE( 467 SizedAlignedArrayOperatorNew->isFromExplicitGlobalModule()); 468 469 // void operator delete(void*) noexcept; 470 auto *Delete = selectFirst<FunctionDecl>( 471 "operator delete", 472 match(functionDecl( 473 hasName("operator delete"), parameterCountIs(1), 474 hasParameter(0, hasType(pointerType(pointee(voidType()))))) 475 .bind("operator delete"), 476 Ctx)); 477 ASSERT_TRUE(Delete->getOwningModule()); 478 EXPECT_TRUE(Delete->isFromExplicitGlobalModule()); 479 480 // void operator delete(void*, std::align_val_t) noexcept; 481 auto *AlignedDelete = selectFirst<FunctionDecl>( 482 "operator delete", 483 match(functionDecl( 484 hasName("operator delete"), parameterCountIs(2), 485 hasParameter(0, hasType(pointerType(pointee(voidType())))), 486 hasParameter(1, hasType(enumDecl(hasName("std::align_val_t"))))) 487 .bind("operator delete"), 488 Ctx)); 489 ASSERT_TRUE(AlignedDelete->getOwningModule()); 490 EXPECT_TRUE(AlignedDelete->isFromExplicitGlobalModule()); 491 492 // Sized deallocation is not enabled by default. So we skip it here. 493 494 // void operator delete[](void*) noexcept; 495 auto *ArrayDelete = selectFirst<FunctionDecl>( 496 "operator delete[]", 497 match(functionDecl( 498 hasName("operator delete[]"), parameterCountIs(1), 499 hasParameter(0, hasType(pointerType(pointee(voidType()))))) 500 .bind("operator delete[]"), 501 Ctx)); 502 ASSERT_TRUE(ArrayDelete->getOwningModule()); 503 EXPECT_TRUE(ArrayDelete->isFromExplicitGlobalModule()); 504 505 // void operator delete[](void*, std::align_val_t) noexcept; 506 auto *AlignedArrayDelete = selectFirst<FunctionDecl>( 507 "operator delete[]", 508 match(functionDecl( 509 hasName("operator delete[]"), parameterCountIs(2), 510 hasParameter(0, hasType(pointerType(pointee(voidType())))), 511 hasParameter(1, hasType(enumDecl(hasName("std::align_val_t"))))) 512 .bind("operator delete[]"), 513 Ctx)); 514 ASSERT_TRUE(AlignedArrayDelete->getOwningModule()); 515 EXPECT_TRUE(AlignedArrayDelete->isFromExplicitGlobalModule()); 516 } 517 518 TEST(Decl, TemplateArgumentDefaulted) { 519 llvm::Annotations Code(R"cpp( 520 template<typename T1, typename T2> 521 struct Alloc {}; 522 523 template <typename T1, 524 typename T2 = double, 525 int T3 = 42, 526 typename T4 = Alloc<T1, T2>> 527 struct Foo { 528 }; 529 530 Foo<char, int, 42, Alloc<char, int>> X; 531 )cpp"); 532 533 auto AST = 534 tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); 535 ASTContext &Ctx = AST->getASTContext(); 536 537 auto const *CTSD = selectFirst<ClassTemplateSpecializationDecl>( 538 "id", 539 match(classTemplateSpecializationDecl(hasName("Foo")).bind("id"), Ctx)); 540 ASSERT_NE(CTSD, nullptr); 541 auto const &ArgList = CTSD->getTemplateArgs(); 542 543 EXPECT_FALSE(ArgList.get(0).getIsDefaulted()); 544 EXPECT_FALSE(ArgList.get(1).getIsDefaulted()); 545 EXPECT_TRUE(ArgList.get(2).getIsDefaulted()); 546 EXPECT_TRUE(ArgList.get(3).getIsDefaulted()); 547 } 548 549 TEST(Decl, CXXDestructorDeclsShouldHaveWellFormedNameInfoRanges) { 550 // GH71161 551 llvm::Annotations Code(R"cpp( 552 template <typename T> struct Resource { 553 ~Resource(); // 1 554 }; 555 template <typename T> 556 Resource<T>::~Resource() {} // 2,3 557 558 void instantiate_template() { 559 Resource<int> x; 560 } 561 )cpp"); 562 563 auto AST = tooling::buildASTFromCode(Code.code()); 564 ASTContext &Ctx = AST->getASTContext(); 565 566 const auto &SM = Ctx.getSourceManager(); 567 auto GetNameInfoRange = [&SM](const BoundNodes &Match) { 568 const auto *D = Match.getNodeAs<CXXDestructorDecl>("dtor"); 569 return D->getNameInfo().getSourceRange().printToString(SM); 570 }; 571 572 auto Matches = match(findAll(cxxDestructorDecl().bind("dtor")), 573 *Ctx.getTranslationUnitDecl(), Ctx); 574 ASSERT_EQ(Matches.size(), 3U); 575 EXPECT_EQ(GetNameInfoRange(Matches[0]), "<input.cc:3:3, col:4>"); 576 EXPECT_EQ(GetNameInfoRange(Matches[1]), "<input.cc:6:14, col:15>"); 577 EXPECT_EQ(GetNameInfoRange(Matches[2]), "<input.cc:6:14, col:15>"); 578 } 579 580 TEST(Decl, CommentsAttachedToDecl1) { 581 const SmallVector<StringRef> Sources{ 582 R"( 583 /// Test comment 584 void f(); 585 )", 586 587 R"( 588 /// Test comment 589 590 void f(); 591 )", 592 593 R"( 594 /// Test comment 595 #if 0 596 // tralala 597 #endif 598 void f(); 599 )", 600 601 R"( 602 /// Test comment 603 604 #if 0 605 // tralala 606 #endif 607 608 void f(); 609 )", 610 611 R"( 612 /// Test comment 613 #ifdef DOCS 614 template<typename T> 615 #endif 616 void f(); 617 )", 618 619 R"( 620 /// Test comment 621 622 #ifdef DOCS 623 template<typename T> 624 #endif 625 626 void f(); 627 )", 628 }; 629 630 for (const auto code : Sources) { 631 auto AST = tooling::buildASTFromCodeWithArgs(code, /*Args=*/{"-std=c++20"}); 632 ASTContext &Ctx = AST->getASTContext(); 633 634 auto const *F = selectFirst<FunctionDecl>( 635 "id", match(functionDecl(hasName("f")).bind("id"), Ctx)); 636 ASSERT_NE(F, nullptr); 637 638 auto const *C = Ctx.getRawCommentForDeclNoCache(F); 639 ASSERT_NE(C, nullptr); 640 EXPECT_EQ(C->getRawText(Ctx.getSourceManager()), "/// Test comment"); 641 } 642 } 643 644 TEST(Decl, CommentsAttachedToDecl2) { 645 const SmallVector<StringRef> Sources{ 646 R"( 647 /** Test comment 648 */ 649 void f(); 650 )", 651 652 R"( 653 /** Test comment 654 */ 655 656 void f(); 657 )", 658 659 R"( 660 /** Test comment 661 */ 662 #if 0 663 /* tralala */ 664 #endif 665 void f(); 666 )", 667 668 R"( 669 /** Test comment 670 */ 671 672 #if 0 673 /* tralala */ 674 #endif 675 676 void f(); 677 )", 678 679 R"( 680 /** Test comment 681 */ 682 #ifdef DOCS 683 template<typename T> 684 #endif 685 void f(); 686 )", 687 688 R"( 689 /** Test comment 690 */ 691 692 #ifdef DOCS 693 template<typename T> 694 #endif 695 696 void f(); 697 )", 698 }; 699 700 for (const auto code : Sources) { 701 auto AST = tooling::buildASTFromCodeWithArgs(code, /*Args=*/{"-std=c++20"}); 702 ASTContext &Ctx = AST->getASTContext(); 703 704 auto const *F = selectFirst<FunctionDecl>( 705 "id", match(functionDecl(hasName("f")).bind("id"), Ctx)); 706 ASSERT_NE(F, nullptr); 707 708 auto const *C = Ctx.getRawCommentForDeclNoCache(F); 709 ASSERT_NE(C, nullptr); 710 EXPECT_EQ(C->getRawText(Ctx.getSourceManager()), 711 "/** Test comment\n */"); 712 } 713 } 714 715 TEST(Decl, CommentsAttachedToDecl3) { 716 const SmallVector<StringRef> Sources{ 717 R"( 718 /// @brief Test comment 719 void f(); 720 )", 721 722 R"( 723 /// @brief Test comment 724 725 void f(); 726 )", 727 728 R"( 729 /// @brief Test comment 730 #if 0 731 // tralala 732 #endif 733 void f(); 734 )", 735 736 R"( 737 /// @brief Test comment 738 739 #if 0 740 // tralala 741 #endif 742 743 void f(); 744 )", 745 746 R"( 747 /// @brief Test comment 748 #ifdef DOCS 749 template<typename T> 750 #endif 751 void f(); 752 )", 753 754 R"( 755 /// @brief Test comment 756 757 #ifdef DOCS 758 template<typename T> 759 #endif 760 761 void f(); 762 )", 763 }; 764 765 for (const auto code : Sources) { 766 auto AST = tooling::buildASTFromCodeWithArgs(code, /*Args=*/{"-std=c++20"}); 767 ASTContext &Ctx = AST->getASTContext(); 768 769 auto const *F = selectFirst<FunctionDecl>( 770 "id", match(functionDecl(hasName("f")).bind("id"), Ctx)); 771 ASSERT_NE(F, nullptr); 772 773 auto const *C = Ctx.getRawCommentForDeclNoCache(F); 774 ASSERT_NE(C, nullptr); 775 EXPECT_EQ(C->getRawText(Ctx.getSourceManager()), "/// @brief Test comment"); 776 } 777 } 778 779 TEST(Decl, CommentsAttachedToDecl4) { 780 const SmallVector<StringRef> Sources{ 781 R"( 782 /** \brief Test comment 783 */ 784 void f(); 785 )", 786 787 R"( 788 /** \brief Test comment 789 */ 790 791 void f(); 792 )", 793 794 R"( 795 /** \brief Test comment 796 */ 797 #if 0 798 /* tralala */ 799 #endif 800 void f(); 801 )", 802 803 R"( 804 /** \brief Test comment 805 */ 806 807 #if 0 808 /* tralala */ 809 #endif 810 811 void f(); 812 )", 813 814 R"( 815 /** \brief Test comment 816 */ 817 #ifdef DOCS 818 template<typename T> 819 #endif 820 void f(); 821 )", 822 823 R"( 824 /** \brief Test comment 825 */ 826 827 #ifdef DOCS 828 template<typename T> 829 #endif 830 831 void f(); 832 )", 833 }; 834 835 for (const auto code : Sources) { 836 auto AST = tooling::buildASTFromCodeWithArgs(code, /*Args=*/{"-std=c++20"}); 837 ASTContext &Ctx = AST->getASTContext(); 838 839 auto const *F = selectFirst<FunctionDecl>( 840 "id", match(functionDecl(hasName("f")).bind("id"), Ctx)); 841 ASSERT_NE(F, nullptr); 842 843 auto const *C = Ctx.getRawCommentForDeclNoCache(F); 844 ASSERT_NE(C, nullptr); 845 EXPECT_EQ(C->getRawText(Ctx.getSourceManager()), 846 "/** \\brief Test comment\n */"); 847 } 848 } 849 850 /// This example intentionally inserts characters between a doc comment and the 851 /// associated declaration to verify that the comment does not become associated 852 /// with the FunctionDecl. 853 /// By default, Clang does not allow for other declarations (aside from 854 /// preprocessor directives, as shown above) to be placed between a doc comment 855 /// and a declaration. 856 TEST(Decl, CommentsAttachedToDecl5) { 857 const SmallVector<StringRef> Sources{ 858 R"( 859 /// Test comment 860 ; 861 void f(); 862 )", 863 864 R"( 865 /// Test comment 866 // @ 867 void f(); 868 )", 869 870 R"( 871 /// Test comment 872 // {} 873 void f(); 874 )", 875 }; 876 877 for (const auto code : Sources) { 878 auto AST = tooling::buildASTFromCodeWithArgs(code, /*Args=*/{"-std=c++20"}); 879 ASTContext &Ctx = AST->getASTContext(); 880 881 auto const *F = selectFirst<FunctionDecl>( 882 "id", match(functionDecl(hasName("f")).bind("id"), Ctx)); 883 ASSERT_NE(F, nullptr); 884 885 auto const *C = Ctx.getRawCommentForDeclNoCache(F); 886 ASSERT_EQ(C, nullptr); 887 } 888 } 889