llvm-project
300 строк · 10.0 Кб
1//===- unittests/AST/TypePrinterTest.cpp --- Type printer tests -----------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains tests for QualType::print() and related methods.
10//
11//===----------------------------------------------------------------------===//
12
13#include "ASTPrint.h"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"19
20using namespace clang;21using namespace ast_matchers;22using namespace tooling;23
24namespace {25
26static void PrintType(raw_ostream &Out, const ASTContext *Context,27const QualType *T,28PrintingPolicyAdjuster PolicyAdjuster) {29assert(T && !T->isNull() && "Expected non-null Type");30PrintingPolicy Policy = Context->getPrintingPolicy();31if (PolicyAdjuster)32PolicyAdjuster(Policy);33T->print(Out, Policy);34}
35
36::testing::AssertionResult37PrintedTypeMatches(StringRef Code, const std::vector<std::string> &Args,38const DeclarationMatcher &NodeMatch,39StringRef ExpectedPrinted,40PrintingPolicyAdjuster PolicyAdjuster) {41return PrintedNodeMatches<QualType>(Code, Args, NodeMatch, ExpectedPrinted,42"", PrintType, PolicyAdjuster);43}
44
45} // unnamed namespace46
47TEST(TypePrinter, TemplateId) {48std::string Code = R"cpp(49namespace N {
50template <typename> struct Type {};
51
52template <typename T>
53void Foo(const Type<T> &Param);
54}
55)cpp";56auto Matcher = parmVarDecl(hasType(qualType().bind("id")));57
58ASSERT_TRUE(PrintedTypeMatches(59Code, {}, Matcher, "const Type<T> &",60[](PrintingPolicy &Policy) { Policy.FullyQualifiedName = false; }));61
62ASSERT_TRUE(PrintedTypeMatches(63Code, {}, Matcher, "const Type<T> &",64[](PrintingPolicy &Policy) { Policy.FullyQualifiedName = true; }));65}
66
67TEST(TypePrinter, TemplateId2) {68std::string Code = R"cpp(69template <template <typename ...> class TemplatedType>
70void func(TemplatedType<int> Param);
71)cpp";72auto Matcher = parmVarDecl(hasType(qualType().bind("id")));73
74// Regression test ensuring we do not segfault getting the QualType as a75// string.76ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher, "<int>",77[](PrintingPolicy &Policy) {78Policy.FullyQualifiedName = true;79Policy.PrintCanonicalTypes = true;80}));81}
82
83TEST(TypePrinter, ParamsUglified) {84llvm::StringLiteral Code = R"cpp(85template <typename _Tp, template <typename> class __f>
86const __f<_Tp&> *A = nullptr;
87)cpp";88auto Clean = [](PrintingPolicy &Policy) {89Policy.CleanUglifiedParameters = true;90};91
92ASSERT_TRUE(PrintedTypeMatches(Code, {},93varDecl(hasType(qualType().bind("id"))),94"const __f<_Tp &> *", nullptr));95ASSERT_TRUE(PrintedTypeMatches(Code, {},96varDecl(hasType(qualType().bind("id"))),97"const f<Tp &> *", Clean));98}
99
100TEST(TypePrinter, SuppressElaboration) {101llvm::StringLiteral Code = R"cpp(102namespace shared {
103namespace a {
104template <typename T>
105struct S {};
106} // namespace a
107namespace b {
108struct Foo {};
109} // namespace b
110using Alias = a::S<b::Foo>;
111} // namespace shared
112)cpp";113
114auto Matcher = typedefNameDecl(hasName("::shared::Alias"),115hasType(qualType().bind("id")));116ASSERT_TRUE(PrintedTypeMatches(117Code, {}, Matcher, "a::S<b::Foo>",118[](PrintingPolicy &Policy) { Policy.FullyQualifiedName = true; }));119ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher,120"shared::a::S<shared::b::Foo>",121[](PrintingPolicy &Policy) {122Policy.SuppressElaboration = true;123Policy.FullyQualifiedName = true;124}));125}
126
127TEST(TypePrinter, TemplateIdWithNTTP) {128constexpr char Code[] = R"cpp(129template <int N>
130struct Str {
131constexpr Str(char const (&s)[N]) { __builtin_memcpy(value, s, N); }
132char value[N];
133};
134template <Str> class ASCII {};
135
136ASCII<"this nontype template argument is too long to print"> x;
137)cpp";138auto Matcher = classTemplateSpecializationDecl(139hasName("ASCII"), has(cxxConstructorDecl(140isMoveConstructor(),141has(parmVarDecl(hasType(qualType().bind("id")))))));142
143ASSERT_TRUE(PrintedTypeMatches(144Code, {"-std=c++20"}, Matcher,145R"(ASCII<Str<52>{"this nontype template argument is [...]"}> &&)",146[](PrintingPolicy &Policy) {147Policy.EntireContentsOfLargeArray = false;148}));149
150ASSERT_TRUE(PrintedTypeMatches(151Code, {"-std=c++20"}, Matcher,152R"(ASCII<Str<52>{"this nontype template argument is too long to print"}> &&)",153[](PrintingPolicy &Policy) {154Policy.EntireContentsOfLargeArray = true;155}));156}
157
158TEST(TypePrinter, TemplateArgumentsSubstitution) {159constexpr char Code[] = R"cpp(160template <typename Y> class X {};
161typedef X<int> A;
162int foo() {
163return sizeof(A);
164}
165)cpp";166auto Matcher = typedefNameDecl(hasName("A"), hasType(qualType().bind("id")));167ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher, "X<int>",168[](PrintingPolicy &Policy) {169Policy.SuppressTagKeyword = false;170Policy.SuppressScope = true;171}));172}
173
174TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {175/// Tests clang::isSubstitutedDefaultArgument on TemplateArguments176/// that are of kind TemplateArgument::Expression177constexpr char Code[] = R"cpp(178constexpr bool func() { return true; }
179
180template <typename T1 = int,
181int T2 = 42,
182T1 T3 = 43,
183int T4 = sizeof(T1),
184bool T5 = func()
185>
186struct Foo {
187};
188
189Foo<int, 40 + 2> X;
190)cpp";191
192auto AST = tooling::buildASTFromCodeWithArgs(Code, /*Args=*/{"-std=c++20"});193ASTContext &Ctx = AST->getASTContext();194
195auto const *CTD = selectFirst<ClassTemplateDecl>(196"id", match(classTemplateDecl(hasName("Foo")).bind("id"), Ctx));197ASSERT_NE(CTD, nullptr);198auto const *CTSD = *CTD->specializations().begin();199ASSERT_NE(CTSD, nullptr);200auto const *Params = CTD->getTemplateParameters();201ASSERT_NE(Params, nullptr);202auto const &ArgList = CTSD->getTemplateArgs();203
204auto createBinOpExpr = [&](uint32_t LHS, uint32_t RHS,205uint32_t Result) -> ConstantExpr * {206const int numBits = 32;207clang::APValue ResultVal{llvm::APSInt(llvm::APInt(numBits, Result))};208auto *LHSInt = IntegerLiteral::Create(Ctx, llvm::APInt(numBits, LHS),209Ctx.UnsignedIntTy, {});210auto *RHSInt = IntegerLiteral::Create(Ctx, llvm::APInt(numBits, RHS),211Ctx.UnsignedIntTy, {});212auto *BinOp = BinaryOperator::Create(213Ctx, LHSInt, RHSInt, BinaryOperatorKind::BO_Add, Ctx.UnsignedIntTy,214ExprValueKind::VK_PRValue, ExprObjectKind::OK_Ordinary, {}, {});215return ConstantExpr::Create(Ctx, dyn_cast<Expr>(BinOp), ResultVal);216};217
218{219// Arg is an integral '42'220auto const &Arg = ArgList.get(1);221ASSERT_EQ(Arg.getKind(), TemplateArgument::Integral);222
223// Param has default expr which evaluates to '42'224auto const *Param = Params->getParam(1);225
226EXPECT_TRUE(clang::isSubstitutedDefaultArgument(227Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));228}229
230{231// Arg is an integral '41'232llvm::APInt Int(32, 41);233TemplateArgument Arg(Ctx, llvm::APSInt(Int), Ctx.UnsignedIntTy);234
235// Param has default expr which evaluates to '42'236auto const *Param = Params->getParam(1);237
238EXPECT_FALSE(clang::isSubstitutedDefaultArgument(239Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));240}241
242{243// Arg is an integral '4'244llvm::APInt Int(32, 4);245TemplateArgument Arg(Ctx, llvm::APSInt(Int), Ctx.UnsignedIntTy);246
247// Param has is value-dependent expression (i.e., sizeof(T))248auto const *Param = Params->getParam(3);249
250EXPECT_FALSE(clang::isSubstitutedDefaultArgument(251Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));252}253
254{255const int LHS = 40;256const int RHS = 2;257const int Result = 42;258auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);259// Arg is instantiated with '40 + 2'260TemplateArgument Arg(ConstExpr);261
262// Param has default expr of '42'263auto const *Param = Params->getParam(1);264
265EXPECT_TRUE(clang::isSubstitutedDefaultArgument(266Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));267}268
269{270const int LHS = 40;271const int RHS = 1;272const int Result = 41;273auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);274
275// Arg is instantiated with '40 + 1'276TemplateArgument Arg(ConstExpr);277
278// Param has default expr of '42'279auto const *Param = Params->getParam(1);280
281EXPECT_FALSE(clang::isSubstitutedDefaultArgument(282Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));283}284
285{286const int LHS = 4;287const int RHS = 0;288const int Result = 4;289auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);290
291// Arg is instantiated with '4 + 0'292TemplateArgument Arg(ConstExpr);293
294// Param has is value-dependent expression (i.e., sizeof(T))295auto const *Param = Params->getParam(3);296
297EXPECT_FALSE(clang::isSubstitutedDefaultArgument(298Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));299}300}
301