1 //===- unittests/AST/DeclTest.cpp --- Declaration tests -------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Unit tests for Decl nodes in the AST.
11 //===----------------------------------------------------------------------===//
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"
28 using namespace clang::ast_matchers
;
29 using namespace clang::tooling
;
30 using namespace clang
;
32 TEST(Decl
, CleansUpAPValues
) {
34 std::unique_ptr
<FrontendActionFactory
> Factory(
35 newFrontendActionFactory(&Finder
));
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
40 std::vector
<std::string
> Args(1, "-std=c++11");
41 Args
.push_back("-fno-ms-extensions");
42 ASSERT_TRUE(runToolOnCodeWithArgs(
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;"
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; }",
64 // FIXME: Once this test starts breaking we can test APValue::needsCleanup
66 ASSERT_FALSE(runToolOnCodeWithArgs(
68 "constexpr _Complex __uint128_t c = 0xffffffffffffffff;",
72 TEST(Decl
, AsmLabelAttr
) {
73 // Create two method decls: `f` and `g`.
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();
88 selectFirst
<CXXRecordDecl
>("d", match(cxxRecordDecl().bind("d"), Ctx
));
89 NamedDecl
*DeclF
= *DeclS
->method_begin();
90 NamedDecl
*DeclG
= *(++DeclS
->method_begin());
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));
96 // Mangle the decl names.
97 std::string MangleF
, MangleG
;
98 std::unique_ptr
<ItaniumMangleContext
> MC(
99 ItaniumMangleContext::create(Ctx
, Diags
));
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
);
107 ASSERT_TRUE(0 == MangleF
.compare("\x01" "foo"));
108 ASSERT_TRUE(0 == MangleG
.compare("goo"));
111 TEST(Decl
, MangleDependentSizedArray
) {
116 template <typename T, int N>
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();
129 selectFirst
<VarDecl
>("A", match(varDecl().bind("A"), Ctx
));
131 selectFirst
<FieldDecl
>("B", match(fieldDecl().bind("B"), Ctx
));
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
));
138 MC
->mangleCanonicalTypeName(DeclA
->getType(), OS_A
);
139 MC
->mangleCanonicalTypeName(DeclB
->getType(), OS_B
);
141 ASSERT_TRUE(0 == MangleA
.compare("_ZTSA_i"));
142 ASSERT_TRUE(0 == MangleB
.compare("_ZTSAT0__T_"));
145 TEST(Decl
, ConceptDecl
) {
146 llvm::StringRef
Code(R
"(
148 concept integral = __is_integral(T);
151 auto AST
= tooling::buildASTFromCodeWithArgs(Code
, {"-std=c++20"});
152 ASTContext
&Ctx
= AST
->getASTContext();
155 selectFirst
<ConceptDecl
>("decl", match(conceptDecl().bind("decl"), Ctx
));
156 ASSERT_TRUE(Decl
!= nullptr);
157 EXPECT_EQ(Decl
->getName(), "integral");
160 TEST(Decl
, EnumDeclRange
) {
161 llvm::Annotations
Code(R
"(
163 [[enum Bar : Foo]];)");
164 auto AST
= tooling::buildASTFromCodeWithArgs(Code
.code(), /*Args=*/{});
165 ASTContext
&Ctx
= AST
->getASTContext();
166 const auto &SM
= Ctx
.getSourceManager();
169 selectFirst
<TagDecl
>("Bar", match(enumDecl().bind("Bar"), Ctx
));
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
);
176 TEST(Decl
, IsInExportDeclContext
) {
177 llvm::Annotations
Code(R
"(
179 export template <class T>
182 tooling::buildASTFromCodeWithArgs(Code
.code(), /*Args=*/{"-std=c++20"});
183 ASTContext
&Ctx
= AST
->getASTContext();
186 selectFirst
<FunctionDecl
>("f", match(functionDecl().bind("f"), Ctx
));
187 EXPECT_TRUE(f
->isInExportDeclContext());
190 TEST(Decl
, InConsistLinkageForTemplates
) {
191 llvm::Annotations
Code(R
"(
193 export template <class T>
199 export template <class T>
207 tooling::buildASTFromCodeWithArgs(Code
.code(), /*Args=*/{"-std=c++20"});
208 ASTContext
&Ctx
= AST
->getASTContext();
210 llvm::SmallVector
<ast_matchers::BoundNodes
, 2> Funcs
=
211 match(functionDecl().bind("f"), Ctx
);
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());
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
);
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());
232 TEST(Decl
, ModuleAndInternalLinkage
) {
233 llvm::Annotations
Code(R
"(
242 tooling::buildASTFromCodeWithArgs(Code
.code(), /*Args=*/{"-std=c++20"});
243 ASTContext
&Ctx
= AST
->getASTContext();
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
));
250 EXPECT_EQ(a
->getFormalLinkage(), Linkage::Internal
);
251 EXPECT_EQ(f
->getFormalLinkage(), Linkage::Internal
);
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
));
258 EXPECT_EQ(b
->getFormalLinkage(), Linkage::Module
);
259 EXPECT_EQ(g
->getFormalLinkage(), Linkage::Module
);
262 TEST(Decl
, GetNonTransparentDeclContext
) {
263 llvm::Annotations
Code(R
"(
265 export template <class> struct X {
266 template <class Self> friend void f(Self &&self) {
272 tooling::buildASTFromCodeWithArgs(Code
.code(), /*Args=*/{"-std=c++20"});
273 ASTContext
&Ctx
= AST
->getASTContext();
275 auto *f
= selectFirst
<FunctionDecl
>(
276 "f", match(functionDecl(hasName("f")).bind("f"), Ctx
));
278 EXPECT_TRUE(f
->getNonTransparentDeclContext()->isFileContext());
281 TEST(Decl
, MemberFunctionInModules
) {
282 llvm::Annotations
Code(R
"(
294 tooling::buildASTFromCodeWithArgs(Code
.code(), /*Args=*/{"-std=c++20"});
295 ASTContext
&Ctx
= AST
->getASTContext();
297 auto *foo
= selectFirst
<FunctionDecl
>(
298 "foo", match(functionDecl(hasName("foo")).bind("foo"), Ctx
));
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());
304 auto *bar
= selectFirst
<FunctionDecl
>(
305 "bar", match(functionDecl(hasName("bar")).bind("bar"), Ctx
));
307 // In global module, the function defined within a class definition is
308 // implicitly inline.
309 EXPECT_TRUE(bar
->isInlined());
312 TEST(Decl
, MemberFunctionInHeaderUnit
) {
313 llvm::Annotations
Code(R
"(
322 auto AST
= tooling::buildASTFromCodeWithArgs(
323 Code
.code(), {"-std=c++20", " -xc++-user-header ", "-emit-header-unit"});
324 ASTContext
&Ctx
= AST
->getASTContext();
326 auto *memFn
= selectFirst
<FunctionDecl
>(
327 "memFn", match(functionDecl(hasName("memFn")).bind("memFn"), Ctx
));
329 EXPECT_TRUE(memFn
->isInlined());
332 TEST(Decl
, FriendFunctionWithinClassInHeaderUnit
) {
333 llvm::Annotations
Code(R
"(
337 foo(int v) : value(v) {}
339 friend int getFooValue(foo f) {
345 auto AST
= tooling::buildASTFromCodeWithArgs(
346 Code
.code(), {"-std=c++20", " -xc++-user-header ", "-emit-header-unit"});
347 ASTContext
&Ctx
= AST
->getASTContext();
349 auto *getFooValue
= selectFirst
<FunctionDecl
>(
351 match(functionDecl(hasName("getFooValue")).bind("getFooValue"), Ctx
));
353 EXPECT_TRUE(getFooValue
->isInlined());
356 TEST(Decl
, FunctionDeclBitsShouldNotOverlapWithCXXConstructorDeclBits
) {
357 llvm::Annotations
Code(R
"(
363 A f() { return A(); }
366 auto AST
= tooling::buildASTFromCodeWithArgs(Code
.code(), {"-std=c++14"});
367 ASTContext
&Ctx
= AST
->getASTContext();
370 hasAnyConstructorInitializer(cxxCtorInitializer(isMemberInitializer()));
372 cxxConstructorDecl(isMoveConstructor(), isImplicit(), HasCtorInit
)
375 auto *ToImpMoveCtor
=
376 selectFirst
<CXXConstructorDecl
>("MoveCtor", match(ImpMoveCtor
, Ctx
));
378 EXPECT_TRUE(ToImpMoveCtor
->getNumCtorInitializers() == 1);
379 EXPECT_FALSE(ToImpMoveCtor
->FriendConstraintRefersToEnclosingTemplate());
382 TEST(Decl
, NoProtoFunctionDeclAttributes
) {
383 llvm::Annotations
Code(R
"(
387 auto AST
= tooling::buildASTFromCodeWithArgs(
389 /*Args=*/{"-target", "i386-apple-darwin", "-x", "objective-c",
391 ASTContext
&Ctx
= AST
->getASTContext();
393 auto *f
= selectFirst
<FunctionDecl
>(
394 "f", match(functionDecl(hasName("f")).bind("f"), Ctx
));
396 const auto *FPT
= f
->getType()->getAs
<FunctionNoProtoType
>();
398 // Functions without prototypes always have 0 initialized qualifiers
399 EXPECT_FALSE(FPT
->isConst());
400 EXPECT_FALSE(FPT
->isVolatile());
401 EXPECT_FALSE(FPT
->isRestrict());
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]).
411 llvm::Annotations
Code(R
"(
415 virtual void hello() = 0;
416 virtual ~Base() = default;
421 tooling::buildASTFromCodeWithArgs(Code
.code(), /*Args=*/{"-std=c++20"});
422 ASTContext
&Ctx
= AST
->getASTContext();
424 // void* operator new(std::size_t);
425 auto *SizedOperatorNew
= selectFirst
<FunctionDecl
>(
427 match(functionDecl(hasName("operator new"), parameterCountIs(1),
428 hasParameter(0, hasType(isUnsignedInteger())))
429 .bind("operator new"),
431 ASSERT_TRUE(SizedOperatorNew
->getOwningModule());
432 EXPECT_TRUE(SizedOperatorNew
->isFromExplicitGlobalModule());
434 // void* operator new(std::size_t, std::align_val_t);
435 auto *SizedAlignedOperatorNew
= selectFirst
<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"),
443 ASSERT_TRUE(SizedAlignedOperatorNew
->getOwningModule());
444 EXPECT_TRUE(SizedAlignedOperatorNew
->isFromExplicitGlobalModule());
446 // void* operator new[](std::size_t);
447 auto *SizedArrayOperatorNew
= selectFirst
<FunctionDecl
>(
449 match(functionDecl(hasName("operator new[]"), parameterCountIs(1),
450 hasParameter(0, hasType(isUnsignedInteger())))
451 .bind("operator new[]"),
453 ASSERT_TRUE(SizedArrayOperatorNew
->getOwningModule());
454 EXPECT_TRUE(SizedArrayOperatorNew
->isFromExplicitGlobalModule());
456 // void* operator new[](std::size_t, std::align_val_t);
457 auto *SizedAlignedArrayOperatorNew
= selectFirst
<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[]"),
465 ASSERT_TRUE(SizedAlignedArrayOperatorNew
->getOwningModule());
467 SizedAlignedArrayOperatorNew
->isFromExplicitGlobalModule());
469 // void operator delete(void*) noexcept;
470 auto *Delete
= selectFirst
<FunctionDecl
>(
473 hasName("operator delete"), parameterCountIs(1),
474 hasParameter(0, hasType(pointerType(pointee(voidType())))))
475 .bind("operator delete"),
477 ASSERT_TRUE(Delete
->getOwningModule());
478 EXPECT_TRUE(Delete
->isFromExplicitGlobalModule());
480 // void operator delete(void*, std::align_val_t) noexcept;
481 auto *AlignedDelete
= selectFirst
<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"),
489 ASSERT_TRUE(AlignedDelete
->getOwningModule());
490 EXPECT_TRUE(AlignedDelete
->isFromExplicitGlobalModule());
492 // Sized deallocation is not enabled by default. So we skip it here.
494 // void operator delete[](void*) noexcept;
495 auto *ArrayDelete
= selectFirst
<FunctionDecl
>(
498 hasName("operator delete[]"), parameterCountIs(1),
499 hasParameter(0, hasType(pointerType(pointee(voidType())))))
500 .bind("operator delete[]"),
502 ASSERT_TRUE(ArrayDelete
->getOwningModule());
503 EXPECT_TRUE(ArrayDelete
->isFromExplicitGlobalModule());
505 // void operator delete[](void*, std::align_val_t) noexcept;
506 auto *AlignedArrayDelete
= selectFirst
<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[]"),
514 ASSERT_TRUE(AlignedArrayDelete
->getOwningModule());
515 EXPECT_TRUE(AlignedArrayDelete
->isFromExplicitGlobalModule());
518 TEST(Decl
, TemplateArgumentDefaulted
) {
519 llvm::Annotations
Code(R
"cpp(
520 template<typename T1, typename T2>
523 template <typename T1,
524 typename T2 = double,
526 typename T4 = Alloc<T1, T2>>
530 Foo<char, int, 42, Alloc<char, int>> X;
534 tooling::buildASTFromCodeWithArgs(Code
.code(), /*Args=*/{"-std=c++20"});
535 ASTContext
&Ctx
= AST
->getASTContext();
537 auto const *CTSD
= selectFirst
<ClassTemplateSpecializationDecl
>(
539 match(classTemplateSpecializationDecl(hasName("Foo")).bind("id"), Ctx
));
540 ASSERT_NE(CTSD
, nullptr);
541 auto const &ArgList
= CTSD
->getTemplateArgs();
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());
549 TEST(Decl
, CXXDestructorDeclsShouldHaveWellFormedNameInfoRanges
) {
551 llvm::Annotations
Code(R
"cpp(
552 template <typename T> struct Resource {
555 template <typename T>
556 Resource<T>::~Resource() {} // 2,3
558 void instantiate_template() {
563 auto AST
= tooling::buildASTFromCode(Code
.code());
564 ASTContext
&Ctx
= AST
->getASTContext();
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
);
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>");