xref: /llvm-project/clang/unittests/AST/TypePrinterTest.cpp (revision 6503b015d4a8971dcb69a63de7d8d203e4be79b6)
108b28986SNathan Ridge //===- unittests/AST/TypePrinterTest.cpp --- Type printer tests -----------===//
208b28986SNathan Ridge //
308b28986SNathan Ridge // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
408b28986SNathan Ridge // See https://llvm.org/LICENSE.txt for license information.
508b28986SNathan Ridge // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
608b28986SNathan Ridge //
708b28986SNathan Ridge //===----------------------------------------------------------------------===//
808b28986SNathan Ridge //
908b28986SNathan Ridge // This file contains tests for QualType::print() and related methods.
1008b28986SNathan Ridge //
1108b28986SNathan Ridge //===----------------------------------------------------------------------===//
1208b28986SNathan Ridge 
1308b28986SNathan Ridge #include "ASTPrint.h"
1408b28986SNathan Ridge #include "clang/AST/ASTContext.h"
1508b28986SNathan Ridge #include "clang/ASTMatchers/ASTMatchFinder.h"
1608b28986SNathan Ridge #include "clang/Tooling/Tooling.h"
1708b28986SNathan Ridge #include "llvm/ADT/SmallString.h"
1808b28986SNathan Ridge #include "gtest/gtest.h"
1908b28986SNathan Ridge 
2008b28986SNathan Ridge using namespace clang;
2108b28986SNathan Ridge using namespace ast_matchers;
2208b28986SNathan Ridge using namespace tooling;
2308b28986SNathan Ridge 
2408b28986SNathan Ridge namespace {
2508b28986SNathan Ridge 
PrintType(raw_ostream & Out,const ASTContext * Context,const QualType * T,PrintingPolicyAdjuster PolicyAdjuster)2608b28986SNathan Ridge static void PrintType(raw_ostream &Out, const ASTContext *Context,
2708b28986SNathan Ridge                       const QualType *T,
2808b28986SNathan Ridge                       PrintingPolicyAdjuster PolicyAdjuster) {
2908b28986SNathan Ridge   assert(T && !T->isNull() && "Expected non-null Type");
3008b28986SNathan Ridge   PrintingPolicy Policy = Context->getPrintingPolicy();
3108b28986SNathan Ridge   if (PolicyAdjuster)
3208b28986SNathan Ridge     PolicyAdjuster(Policy);
3308b28986SNathan Ridge   T->print(Out, Policy);
3408b28986SNathan Ridge }
3508b28986SNathan Ridge 
3608b28986SNathan Ridge ::testing::AssertionResult
PrintedTypeMatches(StringRef Code,const std::vector<std::string> & Args,const DeclarationMatcher & NodeMatch,StringRef ExpectedPrinted,PrintingPolicyAdjuster PolicyAdjuster)3708b28986SNathan Ridge PrintedTypeMatches(StringRef Code, const std::vector<std::string> &Args,
3808b28986SNathan Ridge                    const DeclarationMatcher &NodeMatch,
3908b28986SNathan Ridge                    StringRef ExpectedPrinted,
4008b28986SNathan Ridge                    PrintingPolicyAdjuster PolicyAdjuster) {
4108b28986SNathan Ridge   return PrintedNodeMatches<QualType>(Code, Args, NodeMatch, ExpectedPrinted,
4208b28986SNathan Ridge                                       "", PrintType, PolicyAdjuster);
4308b28986SNathan Ridge }
4408b28986SNathan Ridge 
4508b28986SNathan Ridge } // unnamed namespace
4608b28986SNathan Ridge 
TEST(TypePrinter,TemplateId)4708b28986SNathan Ridge TEST(TypePrinter, TemplateId) {
4808b28986SNathan Ridge   std::string Code = R"cpp(
4908b28986SNathan Ridge     namespace N {
5008b28986SNathan Ridge       template <typename> struct Type {};
5108b28986SNathan Ridge 
5208b28986SNathan Ridge       template <typename T>
5308b28986SNathan Ridge       void Foo(const Type<T> &Param);
5408b28986SNathan Ridge     }
5508b28986SNathan Ridge   )cpp";
5608b28986SNathan Ridge   auto Matcher = parmVarDecl(hasType(qualType().bind("id")));
5708b28986SNathan Ridge 
5808b28986SNathan Ridge   ASSERT_TRUE(PrintedTypeMatches(
5908b28986SNathan Ridge       Code, {}, Matcher, "const Type<T> &",
6008b28986SNathan Ridge       [](PrintingPolicy &Policy) { Policy.FullyQualifiedName = false; }));
6108b28986SNathan Ridge 
6208b28986SNathan Ridge   ASSERT_TRUE(PrintedTypeMatches(
6315f3cd6bSMatheus Izvekov       Code, {}, Matcher, "const Type<T> &",
6408b28986SNathan Ridge       [](PrintingPolicy &Policy) { Policy.FullyQualifiedName = true; }));
6508b28986SNathan Ridge }
6633c3ef2fSSam McCall 
TEST(TypePrinter,TemplateId2)67225b91e6STom Eccles TEST(TypePrinter, TemplateId2) {
68225b91e6STom Eccles   std::string Code = R"cpp(
69225b91e6STom Eccles       template <template <typename ...> class TemplatedType>
70225b91e6STom Eccles       void func(TemplatedType<int> Param);
71225b91e6STom Eccles     )cpp";
72225b91e6STom Eccles   auto Matcher = parmVarDecl(hasType(qualType().bind("id")));
73225b91e6STom Eccles 
74225b91e6STom Eccles   // Regression test ensuring we do not segfault getting the QualType as a
75225b91e6STom Eccles   // string.
76225b91e6STom Eccles   ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher, "<int>",
77225b91e6STom Eccles                                  [](PrintingPolicy &Policy) {
78225b91e6STom Eccles                                    Policy.FullyQualifiedName = true;
79225b91e6STom Eccles                                    Policy.PrintCanonicalTypes = true;
80225b91e6STom Eccles                                  }));
81225b91e6STom Eccles }
82225b91e6STom Eccles 
TEST(TypePrinter,ParamsUglified)8333c3ef2fSSam McCall TEST(TypePrinter, ParamsUglified) {
8433c3ef2fSSam McCall   llvm::StringLiteral Code = R"cpp(
8533c3ef2fSSam McCall     template <typename _Tp, template <typename> class __f>
8633c3ef2fSSam McCall     const __f<_Tp&> *A = nullptr;
8733c3ef2fSSam McCall   )cpp";
8833c3ef2fSSam McCall   auto Clean = [](PrintingPolicy &Policy) {
8933c3ef2fSSam McCall     Policy.CleanUglifiedParameters = true;
9033c3ef2fSSam McCall   };
9133c3ef2fSSam McCall 
9233c3ef2fSSam McCall   ASSERT_TRUE(PrintedTypeMatches(Code, {},
9333c3ef2fSSam McCall                                  varDecl(hasType(qualType().bind("id"))),
9433c3ef2fSSam McCall                                  "const __f<_Tp &> *", nullptr));
9533c3ef2fSSam McCall   ASSERT_TRUE(PrintedTypeMatches(Code, {},
9633c3ef2fSSam McCall                                  varDecl(hasType(qualType().bind("id"))),
9733c3ef2fSSam McCall                                  "const f<Tp &> *", Clean));
9833c3ef2fSSam McCall }
9944eee659SZhihao Yuan 
TEST(TypePrinter,SuppressElaboration)1003cb6ead7SEric Li TEST(TypePrinter, SuppressElaboration) {
1013cb6ead7SEric Li   llvm::StringLiteral Code = R"cpp(
1023cb6ead7SEric Li     namespace shared {
1033cb6ead7SEric Li     namespace a {
1043cb6ead7SEric Li     template <typename T>
1053cb6ead7SEric Li     struct S {};
1063cb6ead7SEric Li     }  // namespace a
1073cb6ead7SEric Li     namespace b {
1083cb6ead7SEric Li     struct Foo {};
1093cb6ead7SEric Li     }  // namespace b
1103cb6ead7SEric Li     using Alias = a::S<b::Foo>;
1113cb6ead7SEric Li     }  // namespace shared
1123cb6ead7SEric Li   )cpp";
1133cb6ead7SEric Li 
1143cb6ead7SEric Li   auto Matcher = typedefNameDecl(hasName("::shared::Alias"),
1153cb6ead7SEric Li                                  hasType(qualType().bind("id")));
1163cb6ead7SEric Li   ASSERT_TRUE(PrintedTypeMatches(
1173cb6ead7SEric Li       Code, {}, Matcher, "a::S<b::Foo>",
1183cb6ead7SEric Li       [](PrintingPolicy &Policy) { Policy.FullyQualifiedName = true; }));
1193cb6ead7SEric Li   ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher,
1203cb6ead7SEric Li                                  "shared::a::S<shared::b::Foo>",
1213cb6ead7SEric Li                                  [](PrintingPolicy &Policy) {
1223cb6ead7SEric Li                                    Policy.SuppressElaboration = true;
1233cb6ead7SEric Li                                    Policy.FullyQualifiedName = true;
1243cb6ead7SEric Li                                  }));
1253cb6ead7SEric Li }
1263cb6ead7SEric Li 
TEST(TypePrinter,TemplateIdWithNTTP)12744eee659SZhihao Yuan TEST(TypePrinter, TemplateIdWithNTTP) {
12844eee659SZhihao Yuan   constexpr char Code[] = R"cpp(
12944eee659SZhihao Yuan     template <int N>
13044eee659SZhihao Yuan     struct Str {
13144eee659SZhihao Yuan       constexpr Str(char const (&s)[N]) { __builtin_memcpy(value, s, N); }
13244eee659SZhihao Yuan       char value[N];
13344eee659SZhihao Yuan     };
13444eee659SZhihao Yuan     template <Str> class ASCII {};
13544eee659SZhihao Yuan 
13644eee659SZhihao Yuan     ASCII<"this nontype template argument is too long to print"> x;
13744eee659SZhihao Yuan   )cpp";
13844eee659SZhihao Yuan   auto Matcher = classTemplateSpecializationDecl(
13944eee659SZhihao Yuan       hasName("ASCII"), has(cxxConstructorDecl(
14044eee659SZhihao Yuan                             isMoveConstructor(),
14144eee659SZhihao Yuan                             has(parmVarDecl(hasType(qualType().bind("id")))))));
14244eee659SZhihao Yuan 
14344eee659SZhihao Yuan   ASSERT_TRUE(PrintedTypeMatches(
14444eee659SZhihao Yuan       Code, {"-std=c++20"}, Matcher,
145835b99e4SNenad Mikša       R"(ASCII<Str<52>{"this nontype template argument is [...]"}> &&)",
14644eee659SZhihao Yuan       [](PrintingPolicy &Policy) {
14744eee659SZhihao Yuan         Policy.EntireContentsOfLargeArray = false;
14844eee659SZhihao Yuan       }));
14944eee659SZhihao Yuan 
15044eee659SZhihao Yuan   ASSERT_TRUE(PrintedTypeMatches(
15144eee659SZhihao Yuan       Code, {"-std=c++20"}, Matcher,
152835b99e4SNenad Mikša       R"(ASCII<Str<52>{"this nontype template argument is too long to print"}> &&)",
15344eee659SZhihao Yuan       [](PrintingPolicy &Policy) {
15444eee659SZhihao Yuan         Policy.EntireContentsOfLargeArray = true;
15544eee659SZhihao Yuan       }));
15644eee659SZhihao Yuan }
157798494edSMichael Buch 
TEST(TypePrinter,TemplateArgumentsSubstitution)158*6503b015SZahira Ammarguellat TEST(TypePrinter, TemplateArgumentsSubstitution) {
159*6503b015SZahira Ammarguellat   constexpr char Code[] = R"cpp(
160*6503b015SZahira Ammarguellat        template <typename Y> class X {};
161*6503b015SZahira Ammarguellat        typedef X<int> A;
162*6503b015SZahira Ammarguellat        int foo() {
163*6503b015SZahira Ammarguellat           return sizeof(A);
164*6503b015SZahira Ammarguellat        }
165*6503b015SZahira Ammarguellat   )cpp";
166*6503b015SZahira Ammarguellat   auto Matcher = typedefNameDecl(hasName("A"), hasType(qualType().bind("id")));
167*6503b015SZahira Ammarguellat   ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher, "X<int>",
168*6503b015SZahira Ammarguellat                                  [](PrintingPolicy &Policy) {
169*6503b015SZahira Ammarguellat                                    Policy.SuppressTagKeyword = false;
170*6503b015SZahira Ammarguellat                                    Policy.SuppressScope = true;
171*6503b015SZahira Ammarguellat                                  }));
172*6503b015SZahira Ammarguellat }
173*6503b015SZahira Ammarguellat 
TEST(TypePrinter,TemplateArgumentsSubstitution_Expressions)174798494edSMichael Buch TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
175798494edSMichael Buch   /// Tests clang::isSubstitutedDefaultArgument on TemplateArguments
176798494edSMichael Buch   /// that are of kind TemplateArgument::Expression
177798494edSMichael Buch   constexpr char Code[] = R"cpp(
178798494edSMichael Buch     constexpr bool func() { return true; }
179798494edSMichael Buch 
180798494edSMichael Buch     template <typename T1 = int,
181798494edSMichael Buch               int      T2 = 42,
182798494edSMichael Buch               T1       T3 = 43,
183798494edSMichael Buch               int      T4 = sizeof(T1),
184798494edSMichael Buch               bool     T5 = func()
185798494edSMichael Buch               >
186798494edSMichael Buch     struct Foo {
187798494edSMichael Buch     };
188798494edSMichael Buch 
189798494edSMichael Buch     Foo<int, 40 + 2> X;
190798494edSMichael Buch   )cpp";
191798494edSMichael Buch 
192798494edSMichael Buch   auto AST = tooling::buildASTFromCodeWithArgs(Code, /*Args=*/{"-std=c++20"});
193798494edSMichael Buch   ASTContext &Ctx = AST->getASTContext();
194798494edSMichael Buch 
195798494edSMichael Buch   auto const *CTD = selectFirst<ClassTemplateDecl>(
196798494edSMichael Buch       "id", match(classTemplateDecl(hasName("Foo")).bind("id"), Ctx));
197798494edSMichael Buch   ASSERT_NE(CTD, nullptr);
198798494edSMichael Buch   auto const *CTSD = *CTD->specializations().begin();
199798494edSMichael Buch   ASSERT_NE(CTSD, nullptr);
200798494edSMichael Buch   auto const *Params = CTD->getTemplateParameters();
201798494edSMichael Buch   ASSERT_NE(Params, nullptr);
202798494edSMichael Buch   auto const &ArgList = CTSD->getTemplateArgs();
203798494edSMichael Buch 
204798494edSMichael Buch   auto createBinOpExpr = [&](uint32_t LHS, uint32_t RHS,
205798494edSMichael Buch                              uint32_t Result) -> ConstantExpr * {
206798494edSMichael Buch     const int numBits = 32;
207798494edSMichael Buch     clang::APValue ResultVal{llvm::APSInt(llvm::APInt(numBits, Result))};
208798494edSMichael Buch     auto *LHSInt = IntegerLiteral::Create(Ctx, llvm::APInt(numBits, LHS),
209798494edSMichael Buch                                           Ctx.UnsignedIntTy, {});
210798494edSMichael Buch     auto *RHSInt = IntegerLiteral::Create(Ctx, llvm::APInt(numBits, RHS),
211798494edSMichael Buch                                           Ctx.UnsignedIntTy, {});
212798494edSMichael Buch     auto *BinOp = BinaryOperator::Create(
213798494edSMichael Buch         Ctx, LHSInt, RHSInt, BinaryOperatorKind::BO_Add, Ctx.UnsignedIntTy,
214798494edSMichael Buch         ExprValueKind::VK_PRValue, ExprObjectKind::OK_Ordinary, {}, {});
215798494edSMichael Buch     return ConstantExpr::Create(Ctx, dyn_cast<Expr>(BinOp), ResultVal);
216798494edSMichael Buch   };
217798494edSMichael Buch 
218798494edSMichael Buch   {
219798494edSMichael Buch     // Arg is an integral '42'
220798494edSMichael Buch     auto const &Arg = ArgList.get(1);
221798494edSMichael Buch     ASSERT_EQ(Arg.getKind(), TemplateArgument::Integral);
222798494edSMichael Buch 
223798494edSMichael Buch     // Param has default expr which evaluates to '42'
224798494edSMichael Buch     auto const *Param = Params->getParam(1);
225798494edSMichael Buch 
226798494edSMichael Buch     EXPECT_TRUE(clang::isSubstitutedDefaultArgument(
227798494edSMichael Buch         Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
228798494edSMichael Buch   }
229798494edSMichael Buch 
230798494edSMichael Buch   {
231798494edSMichael Buch     // Arg is an integral '41'
232798494edSMichael Buch     llvm::APInt Int(32, 41);
233798494edSMichael Buch     TemplateArgument Arg(Ctx, llvm::APSInt(Int), Ctx.UnsignedIntTy);
234798494edSMichael Buch 
235798494edSMichael Buch     // Param has default expr which evaluates to '42'
236798494edSMichael Buch     auto const *Param = Params->getParam(1);
237798494edSMichael Buch 
238798494edSMichael Buch     EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
239798494edSMichael Buch         Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
240798494edSMichael Buch   }
241798494edSMichael Buch 
242798494edSMichael Buch   {
243798494edSMichael Buch     // Arg is an integral '4'
244798494edSMichael Buch     llvm::APInt Int(32, 4);
245798494edSMichael Buch     TemplateArgument Arg(Ctx, llvm::APSInt(Int), Ctx.UnsignedIntTy);
246798494edSMichael Buch 
247798494edSMichael Buch     // Param has is value-dependent expression (i.e., sizeof(T))
248798494edSMichael Buch     auto const *Param = Params->getParam(3);
249798494edSMichael Buch 
250798494edSMichael Buch     EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
251798494edSMichael Buch         Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
252798494edSMichael Buch   }
253798494edSMichael Buch 
254798494edSMichael Buch   {
255798494edSMichael Buch     const int LHS = 40;
256798494edSMichael Buch     const int RHS = 2;
257798494edSMichael Buch     const int Result = 42;
258798494edSMichael Buch     auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
259798494edSMichael Buch     // Arg is instantiated with '40 + 2'
260798494edSMichael Buch     TemplateArgument Arg(ConstExpr);
261798494edSMichael Buch 
262798494edSMichael Buch     // Param has default expr of '42'
263798494edSMichael Buch     auto const *Param = Params->getParam(1);
264798494edSMichael Buch 
265798494edSMichael Buch     EXPECT_TRUE(clang::isSubstitutedDefaultArgument(
266798494edSMichael Buch         Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
267798494edSMichael Buch   }
268798494edSMichael Buch 
269798494edSMichael Buch   {
270798494edSMichael Buch     const int LHS = 40;
271798494edSMichael Buch     const int RHS = 1;
272798494edSMichael Buch     const int Result = 41;
273798494edSMichael Buch     auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
274798494edSMichael Buch 
275798494edSMichael Buch     // Arg is instantiated with '40 + 1'
276798494edSMichael Buch     TemplateArgument Arg(ConstExpr);
277798494edSMichael Buch 
278798494edSMichael Buch     // Param has default expr of '42'
279798494edSMichael Buch     auto const *Param = Params->getParam(1);
280798494edSMichael Buch 
281798494edSMichael Buch     EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
282798494edSMichael Buch         Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
283798494edSMichael Buch   }
284798494edSMichael Buch 
285798494edSMichael Buch   {
286798494edSMichael Buch     const int LHS = 4;
287798494edSMichael Buch     const int RHS = 0;
288798494edSMichael Buch     const int Result = 4;
289798494edSMichael Buch     auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
290798494edSMichael Buch 
291798494edSMichael Buch     // Arg is instantiated with '4 + 0'
292798494edSMichael Buch     TemplateArgument Arg(ConstExpr);
293798494edSMichael Buch 
294798494edSMichael Buch     // Param has is value-dependent expression (i.e., sizeof(T))
295798494edSMichael Buch     auto const *Param = Params->getParam(3);
296798494edSMichael Buch 
297798494edSMichael Buch     EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
298798494edSMichael Buch         Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
299798494edSMichael Buch   }
300798494edSMichael Buch }
301