|  | //===- unittests/AST/DeclTest.cpp --- Declaration tests -------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // Unit tests for Decl nodes in the AST. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/AST/Decl.h" | 
|  | #include "clang/AST/ASTContext.h" | 
|  | #include "clang/AST/DeclCXX.h" | 
|  | #include "clang/AST/DeclTemplate.h" | 
|  | #include "clang/AST/Mangle.h" | 
|  | #include "clang/ASTMatchers/ASTMatchFinder.h" | 
|  | #include "clang/ASTMatchers/ASTMatchers.h" | 
|  | #include "clang/Basic/ABI.h" | 
|  | #include "clang/Basic/Diagnostic.h" | 
|  | #include "clang/Basic/LLVM.h" | 
|  | #include "clang/Basic/TargetInfo.h" | 
|  | #include "clang/Lex/Lexer.h" | 
|  | #include "clang/Tooling/Tooling.h" | 
|  | #include "llvm/IR/DataLayout.h" | 
|  | #include "llvm/Testing/Annotations/Annotations.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace clang::ast_matchers; | 
|  | using namespace clang::tooling; | 
|  | using namespace clang; | 
|  |  | 
|  | TEST(Decl, CleansUpAPValues) { | 
|  | MatchFinder Finder; | 
|  | std::unique_ptr<FrontendActionFactory> Factory( | 
|  | newFrontendActionFactory(&Finder)); | 
|  |  | 
|  | // This is a regression test for a memory leak in APValues for structs that | 
|  | // allocate memory. This test only fails if run under valgrind with full leak | 
|  | // checking enabled. | 
|  | std::vector<std::string> Args(1, "-std=c++11"); | 
|  | Args.push_back("-fno-ms-extensions"); | 
|  | ASSERT_TRUE(runToolOnCodeWithArgs( | 
|  | Factory->create(), | 
|  | "struct X { int a; }; constexpr X x = { 42 };" | 
|  | "union Y { constexpr Y(int a) : a(a) {} int a; }; constexpr Y y = { 42 };" | 
|  | "constexpr int z[2] = { 42, 43 };" | 
|  | "constexpr int __attribute__((vector_size(16))) v1 = {};" | 
|  | "\n#ifdef __SIZEOF_INT128__\n" | 
|  | "constexpr __uint128_t large_int = 0xffffffffffffffff;" | 
|  | "constexpr __uint128_t small_int = 1;" | 
|  | "\n#endif\n" | 
|  | "constexpr double d1 = 42.42;" | 
|  | "constexpr long double d2 = 42.42;" | 
|  | "constexpr _Complex long double c1 = 42.0i;" | 
|  | "constexpr _Complex long double c2 = 42.0;" | 
|  | "template<int N> struct A : A<N-1> {};" | 
|  | "template<> struct A<0> { int n; }; A<50> a;" | 
|  | "constexpr int &r = a.n;" | 
|  | "constexpr int A<50>::*p = &A<50>::n;" | 
|  | "void f() { foo: bar: constexpr int k = __builtin_constant_p(0) ?" | 
|  | "                         (char*)&&foo - (char*)&&bar : 0; }", | 
|  | Args)); | 
|  |  | 
|  | // FIXME: Once this test starts breaking we can test APValue::needsCleanup | 
|  | // for ComplexInt. | 
|  | ASSERT_FALSE(runToolOnCodeWithArgs( | 
|  | Factory->create(), | 
|  | "constexpr _Complex __uint128_t c = 0xffffffffffffffff;", | 
|  | Args)); | 
|  | } | 
|  |  | 
|  | TEST(Decl, AsmLabelAttr) { | 
|  | // Create two method decls: `f` and `g`. | 
|  | StringRef Code = R"( | 
|  | struct S { | 
|  | void f() {} | 
|  | }; | 
|  | )"; | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  | assert(Ctx.getTargetInfo().getUserLabelPrefix() == StringRef("_") && | 
|  | "Expected target to have a global prefix"); | 
|  | DiagnosticsEngine &Diags = AST->getDiagnostics(); | 
|  |  | 
|  | const auto *DeclS = | 
|  | selectFirst<CXXRecordDecl>("d", match(cxxRecordDecl().bind("d"), Ctx)); | 
|  | NamedDecl *DeclF = *DeclS->method_begin(); | 
|  |  | 
|  | DeclF->addAttr(AsmLabelAttr::Create(Ctx, "foo")); | 
|  |  | 
|  | // Mangle the decl names. | 
|  | std::string MangleF; | 
|  | std::unique_ptr<ItaniumMangleContext> MC( | 
|  | ItaniumMangleContext::create(Ctx, Diags)); | 
|  | { | 
|  | llvm::raw_string_ostream OS_F(MangleF); | 
|  | MC->mangleName(DeclF, OS_F); | 
|  | } | 
|  |  | 
|  | ASSERT_EQ(MangleF, "\x01" | 
|  | "foo"); | 
|  | } | 
|  |  | 
|  | TEST(Decl, AsmLabelAttr_LLDB) { | 
|  | StringRef Code = R"( | 
|  | struct S { | 
|  | void f() {} | 
|  | S() = default; | 
|  | ~S() = default; | 
|  | }; | 
|  | )"; | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  | assert(Ctx.getTargetInfo().getUserLabelPrefix() == StringRef("_") && | 
|  | "Expected target to have a global prefix"); | 
|  | DiagnosticsEngine &Diags = AST->getDiagnostics(); | 
|  |  | 
|  | const auto *DeclS = | 
|  | selectFirst<CXXRecordDecl>("d", match(cxxRecordDecl().bind("d"), Ctx)); | 
|  |  | 
|  | auto *DeclF = *DeclS->method_begin(); | 
|  | auto *Ctor = *DeclS->ctor_begin(); | 
|  | auto *Dtor = DeclS->getDestructor(); | 
|  |  | 
|  | ASSERT_TRUE(DeclF); | 
|  | ASSERT_TRUE(Ctor); | 
|  | ASSERT_TRUE(Dtor); | 
|  |  | 
|  | DeclF->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:_Z1fv")); | 
|  | Ctor->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:S")); | 
|  | Dtor->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:~S")); | 
|  |  | 
|  | std::unique_ptr<ItaniumMangleContext> MC( | 
|  | ItaniumMangleContext::create(Ctx, Diags)); | 
|  |  | 
|  | { | 
|  | std::string Mangled; | 
|  | llvm::raw_string_ostream OS_Mangled(Mangled); | 
|  | MC->mangleName(DeclF, OS_Mangled); | 
|  |  | 
|  | ASSERT_EQ(Mangled, "\x01$__lldb_func::123:123:_Z1fv"); | 
|  | }; | 
|  |  | 
|  | { | 
|  | std::string Mangled; | 
|  | llvm::raw_string_ostream OS_Mangled(Mangled); | 
|  | MC->mangleName(GlobalDecl(Ctor, CXXCtorType::Ctor_Complete), OS_Mangled); | 
|  |  | 
|  | ASSERT_EQ(Mangled, "\x01$__lldb_func:C0:123:123:S"); | 
|  | }; | 
|  |  | 
|  | { | 
|  | std::string Mangled; | 
|  | llvm::raw_string_ostream OS_Mangled(Mangled); | 
|  | MC->mangleName(GlobalDecl(Ctor, CXXCtorType::Ctor_Base), OS_Mangled); | 
|  |  | 
|  | ASSERT_EQ(Mangled, "\x01$__lldb_func:C1:123:123:S"); | 
|  | }; | 
|  |  | 
|  | { | 
|  | std::string Mangled; | 
|  | llvm::raw_string_ostream OS_Mangled(Mangled); | 
|  | MC->mangleName(GlobalDecl(Dtor, CXXDtorType::Dtor_Deleting), OS_Mangled); | 
|  |  | 
|  | ASSERT_EQ(Mangled, "\x01$__lldb_func:D0:123:123:~S"); | 
|  | }; | 
|  |  | 
|  | { | 
|  | std::string Mangled; | 
|  | llvm::raw_string_ostream OS_Mangled(Mangled); | 
|  | MC->mangleName(GlobalDecl(Dtor, CXXDtorType::Dtor_Base), OS_Mangled); | 
|  |  | 
|  | ASSERT_EQ(Mangled, "\x01$__lldb_func:D2:123:123:~S"); | 
|  | }; | 
|  | } | 
|  |  | 
|  | TEST(Decl, AsmLabelAttr_LLDB_Inherit) { | 
|  | StringRef Code = R"( | 
|  | struct Base { | 
|  | Base(int x) {} | 
|  | }; | 
|  |  | 
|  | struct Derived : Base { | 
|  | using Base::Base; | 
|  | } d(5); | 
|  | )"; | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  | assert(Ctx.getTargetInfo().getUserLabelPrefix() == StringRef("_") && | 
|  | "Expected target to have a global prefix"); | 
|  | DiagnosticsEngine &Diags = AST->getDiagnostics(); | 
|  |  | 
|  | const auto *Ctor = selectFirst<CXXConstructorDecl>( | 
|  | "ctor", | 
|  | match(cxxConstructorDecl(isInheritingConstructor()).bind("ctor"), Ctx)); | 
|  |  | 
|  | const_cast<CXXConstructorDecl *>(Ctor)->addAttr( | 
|  | AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:Derived")); | 
|  |  | 
|  | std::unique_ptr<ItaniumMangleContext> MC( | 
|  | ItaniumMangleContext::create(Ctx, Diags)); | 
|  |  | 
|  | { | 
|  | std::string Mangled; | 
|  | llvm::raw_string_ostream OS_Mangled(Mangled); | 
|  | MC->mangleName(GlobalDecl(Ctor, CXXCtorType::Ctor_Complete), OS_Mangled); | 
|  |  | 
|  | ASSERT_EQ(Mangled, "\x01$__lldb_func:CI0:123:123:Derived"); | 
|  | }; | 
|  |  | 
|  | { | 
|  | std::string Mangled; | 
|  | llvm::raw_string_ostream OS_Mangled(Mangled); | 
|  | MC->mangleName(GlobalDecl(Ctor, CXXCtorType::Ctor_Base), OS_Mangled); | 
|  |  | 
|  | ASSERT_EQ(Mangled, "\x01$__lldb_func:CI1:123:123:Derived"); | 
|  | }; | 
|  | } | 
|  |  | 
|  | TEST(Decl, MangleDependentSizedArray) { | 
|  | StringRef Code = R"( | 
|  | template <int ...N> | 
|  | int A[] = {N...}; | 
|  |  | 
|  | template <typename T, int N> | 
|  | struct S { | 
|  | T B[N]; | 
|  | }; | 
|  | )"; | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code, {"-target", "i386-apple-darwin"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  | assert(Ctx.getTargetInfo().getUserLabelPrefix() == StringRef("_") && | 
|  | "Expected target to have a global prefix"); | 
|  | DiagnosticsEngine &Diags = AST->getDiagnostics(); | 
|  |  | 
|  | const auto *DeclA = | 
|  | selectFirst<VarDecl>("A", match(varDecl().bind("A"), Ctx)); | 
|  | const auto *DeclB = | 
|  | selectFirst<FieldDecl>("B", match(fieldDecl().bind("B"), Ctx)); | 
|  |  | 
|  | std::string MangleA, MangleB; | 
|  | llvm::raw_string_ostream OS_A(MangleA), OS_B(MangleB); | 
|  | std::unique_ptr<ItaniumMangleContext> MC( | 
|  | ItaniumMangleContext::create(Ctx, Diags)); | 
|  |  | 
|  | MC->mangleCanonicalTypeName(DeclA->getType(), OS_A); | 
|  | MC->mangleCanonicalTypeName(DeclB->getType(), OS_B); | 
|  |  | 
|  | ASSERT_EQ(MangleA, "_ZTSA_i"); | 
|  | ASSERT_EQ(MangleB, "_ZTSAT0__T_"); | 
|  | } | 
|  |  | 
|  | TEST(Decl, ConceptDecl) { | 
|  | llvm::StringRef Code(R"( | 
|  | template<class T> | 
|  | concept integral = __is_integral(T); | 
|  | )"); | 
|  |  | 
|  | auto AST = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++20"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | const auto *Decl = | 
|  | selectFirst<ConceptDecl>("decl", match(conceptDecl().bind("decl"), Ctx)); | 
|  | ASSERT_TRUE(Decl != nullptr); | 
|  | EXPECT_EQ(Decl->getName(), "integral"); | 
|  | } | 
|  |  | 
|  | TEST(Decl, EnumDeclRange) { | 
|  | llvm::Annotations Code(R"( | 
|  | typedef int Foo; | 
|  | [[enum Bar : Foo]];)"); | 
|  | auto AST = tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  | const auto &SM = Ctx.getSourceManager(); | 
|  |  | 
|  | const auto *Bar = | 
|  | selectFirst<TagDecl>("Bar", match(enumDecl().bind("Bar"), Ctx)); | 
|  | auto BarRange = | 
|  | Lexer::getAsCharRange(Bar->getSourceRange(), SM, Ctx.getLangOpts()); | 
|  | EXPECT_EQ(SM.getFileOffset(BarRange.getBegin()), Code.range().Begin); | 
|  | EXPECT_EQ(SM.getFileOffset(BarRange.getEnd()), Code.range().End); | 
|  | } | 
|  |  | 
|  | TEST(Decl, IsInExportDeclContext) { | 
|  | llvm::Annotations Code(R"( | 
|  | export module m; | 
|  | export template <class T> | 
|  | void f() {})"); | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | const auto *f = | 
|  | selectFirst<FunctionDecl>("f", match(functionDecl().bind("f"), Ctx)); | 
|  | EXPECT_TRUE(f->isInExportDeclContext()); | 
|  | } | 
|  |  | 
|  | TEST(Decl, InConsistLinkageForTemplates) { | 
|  | llvm::Annotations Code(R"( | 
|  | export module m; | 
|  | export template <class T> | 
|  | void f() {} | 
|  |  | 
|  | template <> | 
|  | void f<int>() {} | 
|  |  | 
|  | export template <class T> | 
|  | class C {}; | 
|  |  | 
|  | template<> | 
|  | class C<int> {}; | 
|  | )"); | 
|  |  | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | llvm::SmallVector<ast_matchers::BoundNodes, 2> Funcs = | 
|  | match(functionDecl().bind("f"), Ctx); | 
|  |  | 
|  | EXPECT_EQ(Funcs.size(), 2U); | 
|  | const FunctionDecl *TemplateF = Funcs[0].getNodeAs<FunctionDecl>("f"); | 
|  | const FunctionDecl *SpecializedF = Funcs[1].getNodeAs<FunctionDecl>("f"); | 
|  | EXPECT_EQ(TemplateF->getLinkageInternal(), | 
|  | SpecializedF->getLinkageInternal()); | 
|  |  | 
|  | llvm::SmallVector<ast_matchers::BoundNodes, 1> ClassTemplates = | 
|  | match(classTemplateDecl().bind("C"), Ctx); | 
|  | llvm::SmallVector<ast_matchers::BoundNodes, 1> ClassSpecializations = | 
|  | match(classTemplateSpecializationDecl().bind("C"), Ctx); | 
|  |  | 
|  | EXPECT_EQ(ClassTemplates.size(), 1U); | 
|  | EXPECT_EQ(ClassSpecializations.size(), 1U); | 
|  | const NamedDecl *TemplatedC = ClassTemplates[0].getNodeAs<NamedDecl>("C"); | 
|  | const NamedDecl *SpecializedC = ClassSpecializations[0].getNodeAs<NamedDecl>("C"); | 
|  | EXPECT_EQ(TemplatedC->getLinkageInternal(), | 
|  | SpecializedC->getLinkageInternal()); | 
|  | } | 
|  |  | 
|  | TEST(Decl, ModuleAndInternalLinkage) { | 
|  | llvm::Annotations Code(R"( | 
|  | export module M; | 
|  | static int a; | 
|  | static int f(int x); | 
|  |  | 
|  | int b; | 
|  | int g(int x);)"); | 
|  |  | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | const auto *a = | 
|  | selectFirst<VarDecl>("a", match(varDecl(hasName("a")).bind("a"), Ctx)); | 
|  | const auto *f = selectFirst<FunctionDecl>( | 
|  | "f", match(functionDecl(hasName("f")).bind("f"), Ctx)); | 
|  |  | 
|  | EXPECT_EQ(a->getFormalLinkage(), Linkage::Internal); | 
|  | EXPECT_EQ(f->getFormalLinkage(), Linkage::Internal); | 
|  |  | 
|  | const auto *b = | 
|  | selectFirst<VarDecl>("b", match(varDecl(hasName("b")).bind("b"), Ctx)); | 
|  | const auto *g = selectFirst<FunctionDecl>( | 
|  | "g", match(functionDecl(hasName("g")).bind("g"), Ctx)); | 
|  |  | 
|  | EXPECT_EQ(b->getFormalLinkage(), Linkage::Module); | 
|  | EXPECT_EQ(g->getFormalLinkage(), Linkage::Module); | 
|  | } | 
|  |  | 
|  | TEST(Decl, GetNonTransparentDeclContext) { | 
|  | llvm::Annotations Code(R"( | 
|  | export module m3; | 
|  | export template <class> struct X { | 
|  | template <class Self> friend void f(Self &&self) { | 
|  | (Self&)self; | 
|  | } | 
|  | };)"); | 
|  |  | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | auto *f = selectFirst<FunctionDecl>( | 
|  | "f", match(functionDecl(hasName("f")).bind("f"), Ctx)); | 
|  |  | 
|  | EXPECT_TRUE(f->getNonTransparentDeclContext()->isFileContext()); | 
|  | } | 
|  |  | 
|  | TEST(Decl, MemberFunctionInModules) { | 
|  | llvm::Annotations Code(R"( | 
|  | module; | 
|  | class G { | 
|  | void bar() {} | 
|  | }; | 
|  | export module M; | 
|  | class A { | 
|  | void foo() {} | 
|  | }; | 
|  | )"); | 
|  |  | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | auto *foo = selectFirst<FunctionDecl>( | 
|  | "foo", match(functionDecl(hasName("foo")).bind("foo"), Ctx)); | 
|  |  | 
|  | // The function defined within a class definition is not implicitly inline | 
|  | // if it is not attached to global module | 
|  | EXPECT_FALSE(foo->isInlined()); | 
|  |  | 
|  | auto *bar = selectFirst<FunctionDecl>( | 
|  | "bar", match(functionDecl(hasName("bar")).bind("bar"), Ctx)); | 
|  |  | 
|  | // In global module, the function defined within a class definition is | 
|  | // implicitly inline. | 
|  | EXPECT_TRUE(bar->isInlined()); | 
|  | } | 
|  |  | 
|  | TEST(Decl, MemberFunctionInHeaderUnit) { | 
|  | llvm::Annotations Code(R"( | 
|  | class foo { | 
|  | public: | 
|  | int memFn() { | 
|  | return 43; | 
|  | } | 
|  | }; | 
|  | )"); | 
|  |  | 
|  | auto AST = tooling::buildASTFromCodeWithArgs( | 
|  | Code.code(), {"-std=c++20", " -xc++-user-header ", "-emit-header-unit"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | auto *memFn = selectFirst<FunctionDecl>( | 
|  | "memFn", match(functionDecl(hasName("memFn")).bind("memFn"), Ctx)); | 
|  |  | 
|  | EXPECT_TRUE(memFn->isInlined()); | 
|  | } | 
|  |  | 
|  | TEST(Decl, FriendFunctionWithinClassInHeaderUnit) { | 
|  | llvm::Annotations Code(R"( | 
|  | class foo { | 
|  | int value; | 
|  | public: | 
|  | foo(int v) : value(v) {} | 
|  |  | 
|  | friend int getFooValue(foo f) { | 
|  | return f.value; | 
|  | } | 
|  | }; | 
|  | )"); | 
|  |  | 
|  | auto AST = tooling::buildASTFromCodeWithArgs( | 
|  | Code.code(), {"-std=c++20", " -xc++-user-header ", "-emit-header-unit"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | auto *getFooValue = selectFirst<FunctionDecl>( | 
|  | "getFooValue", | 
|  | match(functionDecl(hasName("getFooValue")).bind("getFooValue"), Ctx)); | 
|  |  | 
|  | EXPECT_TRUE(getFooValue->isInlined()); | 
|  | } | 
|  |  | 
|  | TEST(Decl, FunctionDeclBitsShouldNotOverlapWithCXXConstructorDeclBits) { | 
|  | llvm::Annotations Code(R"( | 
|  | struct A { | 
|  | A() : m() {} | 
|  | int m; | 
|  | }; | 
|  |  | 
|  | A f() { return A(); } | 
|  | )"); | 
|  |  | 
|  | auto AST = tooling::buildASTFromCodeWithArgs(Code.code(), {"-std=c++14"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | auto HasCtorInit = | 
|  | hasAnyConstructorInitializer(cxxCtorInitializer(isMemberInitializer())); | 
|  | auto ImpMoveCtor = | 
|  | cxxConstructorDecl(isMoveConstructor(), isImplicit(), HasCtorInit) | 
|  | .bind("MoveCtor"); | 
|  |  | 
|  | auto *ToImpMoveCtor = | 
|  | selectFirst<CXXConstructorDecl>("MoveCtor", match(ImpMoveCtor, Ctx)); | 
|  |  | 
|  | EXPECT_TRUE(ToImpMoveCtor->getNumCtorInitializers() == 1); | 
|  | EXPECT_FALSE(ToImpMoveCtor->FriendConstraintRefersToEnclosingTemplate()); | 
|  | } | 
|  |  | 
|  | TEST(Decl, NoProtoFunctionDeclAttributes) { | 
|  | llvm::Annotations Code(R"( | 
|  | void f(); | 
|  | )"); | 
|  |  | 
|  | auto AST = tooling::buildASTFromCodeWithArgs( | 
|  | Code.code(), | 
|  | /*Args=*/{"-target", "i386-apple-darwin", "-x", "objective-c", | 
|  | "-std=c89"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | auto *f = selectFirst<FunctionDecl>( | 
|  | "f", match(functionDecl(hasName("f")).bind("f"), Ctx)); | 
|  |  | 
|  | const auto *FPT = f->getType()->getAs<FunctionNoProtoType>(); | 
|  |  | 
|  | // Functions without prototypes always have 0 initialized qualifiers | 
|  | EXPECT_FALSE(FPT->isConst()); | 
|  | EXPECT_FALSE(FPT->isVolatile()); | 
|  | EXPECT_FALSE(FPT->isRestrict()); | 
|  | } | 
|  |  | 
|  | TEST(Decl, ImplicitlyDeclaredAllocationFunctionsInModules) { | 
|  | // C++ [basic.stc.dynamic.general]p2: | 
|  | //   The library provides default definitions for the global allocation | 
|  | //   and deallocation functions. Some global allocation and deallocation | 
|  | //   functions are replaceable ([new.delete]); these are attached to the | 
|  | //   global module ([module.unit]). | 
|  |  | 
|  | llvm::Annotations Code(R"( | 
|  | export module base; | 
|  |  | 
|  | export struct Base { | 
|  | virtual void hello() = 0; | 
|  | virtual ~Base() = default; | 
|  | }; | 
|  | )"); | 
|  |  | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | // void* operator new(std::size_t); | 
|  | auto *SizedOperatorNew = selectFirst<FunctionDecl>( | 
|  | "operator new", | 
|  | match(functionDecl(hasName("operator new"), parameterCountIs(1), | 
|  | hasParameter(0, hasType(isUnsignedInteger()))) | 
|  | .bind("operator new"), | 
|  | Ctx)); | 
|  | ASSERT_TRUE(SizedOperatorNew->getOwningModule()); | 
|  | EXPECT_TRUE(SizedOperatorNew->isFromExplicitGlobalModule()); | 
|  |  | 
|  | // void* operator new(std::size_t, std::align_val_t); | 
|  | auto *SizedAlignedOperatorNew = selectFirst<FunctionDecl>( | 
|  | "operator new", | 
|  | match(functionDecl( | 
|  | hasName("operator new"), parameterCountIs(2), | 
|  | hasParameter(0, hasType(isUnsignedInteger())), | 
|  | hasParameter(1, hasType(enumDecl(hasName("std::align_val_t"))))) | 
|  | .bind("operator new"), | 
|  | Ctx)); | 
|  | ASSERT_TRUE(SizedAlignedOperatorNew->getOwningModule()); | 
|  | EXPECT_TRUE(SizedAlignedOperatorNew->isFromExplicitGlobalModule()); | 
|  |  | 
|  | // void* operator new[](std::size_t); | 
|  | auto *SizedArrayOperatorNew = selectFirst<FunctionDecl>( | 
|  | "operator new[]", | 
|  | match(functionDecl(hasName("operator new[]"), parameterCountIs(1), | 
|  | hasParameter(0, hasType(isUnsignedInteger()))) | 
|  | .bind("operator new[]"), | 
|  | Ctx)); | 
|  | ASSERT_TRUE(SizedArrayOperatorNew->getOwningModule()); | 
|  | EXPECT_TRUE(SizedArrayOperatorNew->isFromExplicitGlobalModule()); | 
|  |  | 
|  | // void* operator new[](std::size_t, std::align_val_t); | 
|  | auto *SizedAlignedArrayOperatorNew = selectFirst<FunctionDecl>( | 
|  | "operator new[]", | 
|  | match(functionDecl( | 
|  | hasName("operator new[]"), parameterCountIs(2), | 
|  | hasParameter(0, hasType(isUnsignedInteger())), | 
|  | hasParameter(1, hasType(enumDecl(hasName("std::align_val_t"))))) | 
|  | .bind("operator new[]"), | 
|  | Ctx)); | 
|  | ASSERT_TRUE(SizedAlignedArrayOperatorNew->getOwningModule()); | 
|  | EXPECT_TRUE( | 
|  | SizedAlignedArrayOperatorNew->isFromExplicitGlobalModule()); | 
|  |  | 
|  | // void operator delete(void*) noexcept; | 
|  | auto *Delete = selectFirst<FunctionDecl>( | 
|  | "operator delete", | 
|  | match(functionDecl( | 
|  | hasName("operator delete"), parameterCountIs(1), | 
|  | hasParameter(0, hasType(pointerType(pointee(voidType()))))) | 
|  | .bind("operator delete"), | 
|  | Ctx)); | 
|  | ASSERT_TRUE(Delete->getOwningModule()); | 
|  | EXPECT_TRUE(Delete->isFromExplicitGlobalModule()); | 
|  |  | 
|  | // void operator delete(void*, std::align_val_t) noexcept; | 
|  | auto *AlignedDelete = selectFirst<FunctionDecl>( | 
|  | "operator delete", | 
|  | match(functionDecl( | 
|  | hasName("operator delete"), parameterCountIs(2), | 
|  | hasParameter(0, hasType(pointerType(pointee(voidType())))), | 
|  | hasParameter(1, hasType(enumDecl(hasName("std::align_val_t"))))) | 
|  | .bind("operator delete"), | 
|  | Ctx)); | 
|  | ASSERT_TRUE(AlignedDelete->getOwningModule()); | 
|  | EXPECT_TRUE(AlignedDelete->isFromExplicitGlobalModule()); | 
|  |  | 
|  | // Sized deallocation is not enabled by default. So we skip it here. | 
|  |  | 
|  | // void operator delete[](void*) noexcept; | 
|  | auto *ArrayDelete = selectFirst<FunctionDecl>( | 
|  | "operator delete[]", | 
|  | match(functionDecl( | 
|  | hasName("operator delete[]"), parameterCountIs(1), | 
|  | hasParameter(0, hasType(pointerType(pointee(voidType()))))) | 
|  | .bind("operator delete[]"), | 
|  | Ctx)); | 
|  | ASSERT_TRUE(ArrayDelete->getOwningModule()); | 
|  | EXPECT_TRUE(ArrayDelete->isFromExplicitGlobalModule()); | 
|  |  | 
|  | // void operator delete[](void*, std::align_val_t) noexcept; | 
|  | auto *AlignedArrayDelete = selectFirst<FunctionDecl>( | 
|  | "operator delete[]", | 
|  | match(functionDecl( | 
|  | hasName("operator delete[]"), parameterCountIs(2), | 
|  | hasParameter(0, hasType(pointerType(pointee(voidType())))), | 
|  | hasParameter(1, hasType(enumDecl(hasName("std::align_val_t"))))) | 
|  | .bind("operator delete[]"), | 
|  | Ctx)); | 
|  | ASSERT_TRUE(AlignedArrayDelete->getOwningModule()); | 
|  | EXPECT_TRUE(AlignedArrayDelete->isFromExplicitGlobalModule()); | 
|  | } | 
|  |  | 
|  | TEST(Decl, TemplateArgumentDefaulted) { | 
|  | llvm::Annotations Code(R"cpp( | 
|  | template<typename T1, typename T2> | 
|  | struct Alloc {}; | 
|  |  | 
|  | template <typename T1, | 
|  | typename T2 = double, | 
|  | int      T3 = 42, | 
|  | typename T4 = Alloc<T1, T2>> | 
|  | struct Foo { | 
|  | }; | 
|  |  | 
|  | Foo<char, int, 42, Alloc<char, int>> X; | 
|  | )cpp"); | 
|  |  | 
|  | auto AST = | 
|  | tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"}); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | auto const *CTSD = selectFirst<ClassTemplateSpecializationDecl>( | 
|  | "id", | 
|  | match(classTemplateSpecializationDecl(hasName("Foo")).bind("id"), Ctx)); | 
|  | ASSERT_NE(CTSD, nullptr); | 
|  | auto const &ArgList = CTSD->getTemplateArgs(); | 
|  |  | 
|  | EXPECT_FALSE(ArgList.get(0).getIsDefaulted()); | 
|  | EXPECT_FALSE(ArgList.get(1).getIsDefaulted()); | 
|  | EXPECT_TRUE(ArgList.get(2).getIsDefaulted()); | 
|  | EXPECT_TRUE(ArgList.get(3).getIsDefaulted()); | 
|  | } | 
|  |  | 
|  | TEST(Decl, CXXDestructorDeclsShouldHaveWellFormedNameInfoRanges) { | 
|  | // GH71161 | 
|  | llvm::Annotations Code(R"cpp( | 
|  | template <typename T> struct Resource { | 
|  | ~Resource(); // 1 | 
|  | }; | 
|  | template <typename T> | 
|  | Resource<T>::~Resource() {} // 2,3 | 
|  |  | 
|  | void instantiate_template() { | 
|  | Resource<int> x; | 
|  | } | 
|  | )cpp"); | 
|  |  | 
|  | auto AST = tooling::buildASTFromCode(Code.code()); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | const auto &SM = Ctx.getSourceManager(); | 
|  | auto GetNameInfoRange = [&SM](const BoundNodes &Match) { | 
|  | const auto *D = Match.getNodeAs<CXXDestructorDecl>("dtor"); | 
|  | return D->getNameInfo().getSourceRange().printToString(SM); | 
|  | }; | 
|  |  | 
|  | auto Matches = match(findAll(cxxDestructorDecl().bind("dtor")), | 
|  | *Ctx.getTranslationUnitDecl(), Ctx); | 
|  | ASSERT_EQ(Matches.size(), 3U); | 
|  | EXPECT_EQ(GetNameInfoRange(Matches[0]), "<input.cc:3:3, col:4>"); | 
|  | EXPECT_EQ(GetNameInfoRange(Matches[1]), "<input.cc:6:14, col:15>"); | 
|  | EXPECT_EQ(GetNameInfoRange(Matches[2]), "<input.cc:6:14, col:15>"); | 
|  | } | 
|  |  | 
|  | TEST(Decl, getQualifiedNameAsString) { | 
|  | llvm::Annotations Code(R"cpp( | 
|  | namespace x::y { | 
|  | template <class T> class Foo { Foo() {} }; | 
|  | } | 
|  | )cpp"); | 
|  |  | 
|  | auto AST = tooling::buildASTFromCode(Code.code()); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | auto const *FD = selectFirst<CXXConstructorDecl>( | 
|  | "ctor", match(cxxConstructorDecl().bind("ctor"), Ctx)); | 
|  | ASSERT_NE(FD, nullptr); | 
|  | ASSERT_EQ(FD->getQualifiedNameAsString(), "x::y::Foo::Foo<T>"); | 
|  | } | 
|  |  | 
|  | TEST(Decl, NoWrittenArgsInImplicitlyInstantiatedVarSpec) { | 
|  | const char *Code = R"cpp( | 
|  | template <typename> | 
|  | int VarTpl; | 
|  |  | 
|  | void fn() { | 
|  | (void)VarTpl<char>; | 
|  | } | 
|  | )cpp"; | 
|  |  | 
|  | auto AST = tooling::buildASTFromCode(Code); | 
|  | ASTContext &Ctx = AST->getASTContext(); | 
|  |  | 
|  | const auto *VTSD = selectFirst<VarTemplateSpecializationDecl>( | 
|  | "id", match(varDecl(isTemplateInstantiation()).bind("id"), Ctx)); | 
|  | ASSERT_NE(VTSD, nullptr); | 
|  | EXPECT_EQ(VTSD->getTemplateArgsAsWritten(), nullptr); | 
|  | } |