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
) {
159 constexpr char Code
[] = R
"cpp(
160 template <typename Y> class X {};
166 auto Matcher
= typedefNameDecl(hasName("A"), hasType(qualType().bind("id")));
167 ASSERT_TRUE(PrintedTypeMatches(Code
, {}, Matcher
, "X<int>",
168 [](PrintingPolicy
&Policy
) {
169 Policy
.SuppressTagKeyword
= false;
170 Policy
.SuppressScope
= true;
174 TEST(TypePrinter
, TemplateArgumentsSubstitution_Expressions
) {
175 /// Tests clang::isSubstitutedDefaultArgument on TemplateArguments
176 /// that are of kind TemplateArgument::Expression
177 constexpr char Code
[] = R
"cpp(
178 constexpr bool func() { return true; }
180 template <typename T1 = int,
192 auto AST
= tooling::buildASTFromCodeWithArgs(Code
, /*Args=*/{"-std=c++20"});
193 ASTContext
&Ctx
= AST
->getASTContext();
195 auto const *CTD
= selectFirst
<ClassTemplateDecl
>(
196 "id", match(classTemplateDecl(hasName("Foo")).bind("id"), Ctx
));
197 ASSERT_NE(CTD
, nullptr);
198 auto const *CTSD
= *CTD
->specializations().begin();
199 ASSERT_NE(CTSD
, nullptr);
200 auto const *Params
= CTD
->getTemplateParameters();
201 ASSERT_NE(Params
, nullptr);
202 auto const &ArgList
= CTSD
->getTemplateArgs();
204 auto createBinOpExpr
= [&](uint32_t LHS
, uint32_t RHS
,
205 uint32_t Result
) -> ConstantExpr
* {
206 const int numBits
= 32;
207 clang::APValue ResultVal
{llvm::APSInt(llvm::APInt(numBits
, Result
))};
208 auto *LHSInt
= IntegerLiteral::Create(Ctx
, llvm::APInt(numBits
, LHS
),
209 Ctx
.UnsignedIntTy
, {});
210 auto *RHSInt
= IntegerLiteral::Create(Ctx
, llvm::APInt(numBits
, RHS
),
211 Ctx
.UnsignedIntTy
, {});
212 auto *BinOp
= BinaryOperator::Create(
213 Ctx
, LHSInt
, RHSInt
, BinaryOperatorKind::BO_Add
, Ctx
.UnsignedIntTy
,
214 ExprValueKind::VK_PRValue
, ExprObjectKind::OK_Ordinary
, {}, {});
215 return ConstantExpr::Create(Ctx
, dyn_cast
<Expr
>(BinOp
), ResultVal
);
219 // Arg is an integral '42'
220 auto const &Arg
= ArgList
.get(1);
221 ASSERT_EQ(Arg
.getKind(), TemplateArgument::Integral
);
223 // Param has default expr which evaluates to '42'
224 auto const *Param
= Params
->getParam(1);
226 EXPECT_TRUE(clang::isSubstitutedDefaultArgument(
227 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));
231 // Arg is an integral '41'
232 llvm::APInt
Int(32, 41);
233 TemplateArgument
Arg(Ctx
, llvm::APSInt(Int
), Ctx
.UnsignedIntTy
);
235 // Param has default expr which evaluates to '42'
236 auto const *Param
= Params
->getParam(1);
238 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
239 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));
243 // Arg is an integral '4'
244 llvm::APInt
Int(32, 4);
245 TemplateArgument
Arg(Ctx
, llvm::APSInt(Int
), Ctx
.UnsignedIntTy
);
247 // Param has is value-dependent expression (i.e., sizeof(T))
248 auto const *Param
= Params
->getParam(3);
250 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
251 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));
257 const int Result
= 42;
258 auto *ConstExpr
= createBinOpExpr(LHS
, RHS
, Result
);
259 // Arg is instantiated with '40 + 2'
260 TemplateArgument
Arg(ConstExpr
);
262 // Param has default expr of '42'
263 auto const *Param
= Params
->getParam(1);
265 EXPECT_TRUE(clang::isSubstitutedDefaultArgument(
266 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));
272 const int Result
= 41;
273 auto *ConstExpr
= createBinOpExpr(LHS
, RHS
, Result
);
275 // Arg is instantiated with '40 + 1'
276 TemplateArgument
Arg(ConstExpr
);
278 // Param has default expr of '42'
279 auto const *Param
= Params
->getParam(1);
281 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
282 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));
288 const int Result
= 4;
289 auto *ConstExpr
= createBinOpExpr(LHS
, RHS
, Result
);
291 // Arg is instantiated with '4 + 0'
292 TemplateArgument
Arg(ConstExpr
);
294 // Param has is value-dependent expression (i.e., sizeof(T))
295 auto const *Param
= Params
->getParam(3);
297 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
298 Ctx
, Arg
, Param
, ArgList
.asArray(), Params
->getDepth()));