1 //===- unittests/AST/TypePrinterTest.cpp --- Type printer 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 // This file contains tests for QualType::print() and related methods.
11 //===----------------------------------------------------------------------===//
14 #include "clang/AST/ASTContext.h"
15 #include "clang/ASTMatchers/ASTMatchFinder.h"
16 #include "clang/Tooling/Tooling.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "gtest/gtest.h"
20 using namespace clang
;
21 using namespace ast_matchers
;
22 using namespace tooling
;
26 static void PrintType(raw_ostream
&Out
, const ASTContext
*Context
,
28 PrintingPolicyAdjuster PolicyAdjuster
) {
29 assert(T
&& !T
->isNull() && "Expected non-null Type");
30 PrintingPolicy Policy
= Context
->getPrintingPolicy();
32 PolicyAdjuster(Policy
);
33 T
->print(Out
, Policy
);
36 ::testing::AssertionResult
37 PrintedTypeMatches(StringRef Code
, const std::vector
<std::string
> &Args
,
38 const DeclarationMatcher
&NodeMatch
,
39 StringRef ExpectedPrinted
,
40 PrintingPolicyAdjuster PolicyAdjuster
) {
41 return PrintedNodeMatches
<QualType
>(Code
, Args
, NodeMatch
, ExpectedPrinted
,
42 "", PrintType
, PolicyAdjuster
);
45 } // unnamed namespace
47 TEST(TypePrinter
, TemplateId
) {
48 std::string Code
= R
"cpp(
50 template <typename> struct Type {};
53 void Foo(const Type<T> &Param);
56 auto Matcher
= parmVarDecl(hasType(qualType().bind("id")));
58 ASSERT_TRUE(PrintedTypeMatches(
59 Code
, {}, Matcher
, "const Type<T> &",
60 [](PrintingPolicy
&Policy
) { Policy
.FullyQualifiedName
= false; }));
62 ASSERT_TRUE(PrintedTypeMatches(
63 Code
, {}, Matcher
, "const Type<T> &",
64 [](PrintingPolicy
&Policy
) { Policy
.FullyQualifiedName
= true; }));
67 TEST(TypePrinter
, TemplateId2
) {
68 std::string Code
= R
"cpp(
69 template <template <typename ...> class TemplatedType>
70 void func(TemplatedType<int> Param);
72 auto Matcher
= parmVarDecl(hasType(qualType().bind("id")));
74 // Regression test ensuring we do not segfault getting the QualType as a
76 ASSERT_TRUE(PrintedTypeMatches(Code
, {}, Matcher
, "<int>",
77 [](PrintingPolicy
&Policy
) {
78 Policy
.FullyQualifiedName
= true;
79 Policy
.PrintCanonicalTypes
= true;
83 TEST(TypePrinter
, ParamsUglified
) {
84 llvm::StringLiteral Code
= R
"cpp(
85 template <typename _Tp, template <typename> class __f>
86 const __f<_Tp&> *A = nullptr;
88 auto Clean
= [](PrintingPolicy
&Policy
) {
89 Policy
.CleanUglifiedParameters
= true;
92 ASSERT_TRUE(PrintedTypeMatches(Code
, {},
93 varDecl(hasType(qualType().bind("id"))),
94 "const __f<_Tp &> *", nullptr));
95 ASSERT_TRUE(PrintedTypeMatches(Code
, {},
96 varDecl(hasType(qualType().bind("id"))),
97 "const f<Tp &> *", Clean
));
100 TEST(TypePrinter
, SuppressElaboration
) {
101 llvm::StringLiteral Code
= R
"cpp(
104 template <typename T>
110 using Alias = a::S<b::Foo>;
111 } // namespace shared
114 auto Matcher
= typedefNameDecl(hasName("::shared::Alias"),
115 hasType(qualType().bind("id")));
116 ASSERT_TRUE(PrintedTypeMatches(
117 Code
, {}, Matcher
, "a::S<b::Foo>",
118 [](PrintingPolicy
&Policy
) { Policy
.FullyQualifiedName
= true; }));
119 ASSERT_TRUE(PrintedTypeMatches(Code
, {}, Matcher
,
120 "shared::a::S<shared::b::Foo>",
121 [](PrintingPolicy
&Policy
) {
122 Policy
.SuppressElaboration
= true;
123 Policy
.FullyQualifiedName
= true;
127 TEST(TypePrinter
, TemplateIdWithNTTP
) {
128 constexpr char Code
[] = R
"cpp(
131 constexpr Str(char const (&s)[N]) { __builtin_memcpy(value, s, N); }
134 template <Str> class ASCII {};
136 ASCII<"this nontype
template argument is too
long to print
"> x;
138 auto Matcher
= classTemplateSpecializationDecl(
139 hasName("ASCII"), has(cxxConstructorDecl(
141 has(parmVarDecl(hasType(qualType().bind("id")))))));
143 ASSERT_TRUE(PrintedTypeMatches(
144 Code
, {"-std=c++20"}, Matcher
,
145 R
"(ASCII<Str<52>{"this nontype
template argument is
[...]"}> &&)",
146 [](PrintingPolicy
&Policy
) {
147 Policy
.EntireContentsOfLargeArray
= false;
150 ASSERT_TRUE(PrintedTypeMatches(
151 Code
, {"-std=c++20"}, Matcher
,
152 R
"(ASCII<Str<52>{"this nontype
template argument is too
long to print
"}> &&)",
153 [](PrintingPolicy
&Policy
) {
154 Policy
.EntireContentsOfLargeArray
= true;
158 TEST(TypePrinter
, TemplateArgumentsSubstitution_Expressions
) {
159 /// Tests clang::isSubstitutedDefaultArgument on TemplateArguments
160 /// that are of kind TemplateArgument::Expression
161 constexpr char Code
[] = R
"cpp(
162 constexpr bool func() { return true; }
164 template <typename T1 = int,
176 auto AST
= tooling::buildASTFromCodeWithArgs(Code
, /*Args=*/{"-std=c++20"});
177 ASTContext
&Ctx
= AST
->getASTContext();
179 auto const *CTD
= selectFirst
<ClassTemplateDecl
>(
180 "id", match(classTemplateDecl(hasName("Foo")).bind("id"), Ctx
));
181 ASSERT_NE(CTD
, nullptr);
182 auto const *CTSD
= *CTD
->specializations().begin();
183 ASSERT_NE(CTSD
, nullptr);
184 auto const *Params
= CTD
->getTemplateParameters();
185 ASSERT_NE(Params
, nullptr);
186 auto const &ArgList
= CTSD
->getTemplateArgs();
188 auto createBinOpExpr
= [&](uint32_t LHS
, uint32_t RHS
,
189 uint32_t Result
) -> ConstantExpr
* {
190 const int numBits
= 32;
191 clang::APValue ResultVal
{llvm::APSInt(llvm::APInt(numBits
, Result
))};
192 auto *LHSInt
= IntegerLiteral::Create(Ctx
, llvm::APInt(numBits
, LHS
),
193 Ctx
.UnsignedIntTy
, {});
194 auto *RHSInt
= IntegerLiteral::Create(Ctx
, llvm::APInt(numBits
, RHS
),
195 Ctx
.UnsignedIntTy
, {});
196 auto *BinOp
= BinaryOperator::Create(
197 Ctx
, LHSInt
, RHSInt
, BinaryOperatorKind::BO_Add
, Ctx
.UnsignedIntTy
,
198 ExprValueKind::VK_PRValue
, ExprObjectKind::OK_Ordinary
, {}, {});
199 return ConstantExpr::Create(Ctx
, dyn_cast
<Expr
>(BinOp
), ResultVal
);
203 // Arg is an integral '42'
204 auto const &Arg
= ArgList
.get(1);
205 ASSERT_EQ(Arg
.getKind(), TemplateArgument::Integral
);
207 // Param has default expr which evaluates to '42'
208 auto const *Param
= Params
->getParam(1);
210 EXPECT_TRUE(clang::isSubstitutedDefaultArgument(
211 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));
215 // Arg is an integral '41'
216 llvm::APInt
Int(32, 41);
217 TemplateArgument
Arg(Ctx
, llvm::APSInt(Int
), Ctx
.UnsignedIntTy
);
219 // Param has default expr which evaluates to '42'
220 auto const *Param
= Params
->getParam(1);
222 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
223 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));
227 // Arg is an integral '4'
228 llvm::APInt
Int(32, 4);
229 TemplateArgument
Arg(Ctx
, llvm::APSInt(Int
), Ctx
.UnsignedIntTy
);
231 // Param has is value-dependent expression (i.e., sizeof(T))
232 auto const *Param
= Params
->getParam(3);
234 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
235 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));
241 const int Result
= 42;
242 auto *ConstExpr
= createBinOpExpr(LHS
, RHS
, Result
);
243 // Arg is instantiated with '40 + 2'
244 TemplateArgument
Arg(ConstExpr
);
246 // Param has default expr of '42'
247 auto const *Param
= Params
->getParam(1);
249 EXPECT_TRUE(clang::isSubstitutedDefaultArgument(
250 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));
256 const int Result
= 41;
257 auto *ConstExpr
= createBinOpExpr(LHS
, RHS
, Result
);
259 // Arg is instantiated with '40 + 1'
260 TemplateArgument
Arg(ConstExpr
);
262 // Param has default expr of '42'
263 auto const *Param
= Params
->getParam(1);
265 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
266 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));
272 const int Result
= 4;
273 auto *ConstExpr
= createBinOpExpr(LHS
, RHS
, Result
);
275 // Arg is instantiated with '4 + 0'
276 TemplateArgument
Arg(ConstExpr
);
278 // Param has is value-dependent expression (i.e., sizeof(T))
279 auto const *Param
= Params
->getParam(3);
281 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
282 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));