363 lines · cpp
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-exception6//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,27 const QualType *T,28 PrintingPolicyAdjuster PolicyAdjuster) {29 assert(T && !T->isNull() && "Expected non-null Type");30 PrintingPolicy Policy = Context->getPrintingPolicy();31 if (PolicyAdjuster)32 PolicyAdjuster(Policy);33 T->print(Out, Policy);34}35 36::testing::AssertionResult37PrintedTypeMatches(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);43}44 45} // unnamed namespace46 47TEST(TypePrinter, TemplateId) {48 std::string Code = R"cpp(49 namespace N {50 template <typename> struct Type {};51 52 template <typename T>53 void Foo(const Type<T> &Param);54 }55 )cpp";56 auto Matcher = parmVarDecl(hasType(qualType().bind("id")));57 58 ASSERT_TRUE(PrintedTypeMatches(59 Code, {}, Matcher, "const Type<T> &",60 [](PrintingPolicy &Policy) { Policy.FullyQualifiedName = false; }));61 62 ASSERT_TRUE(PrintedTypeMatches(63 Code, {}, Matcher, "const N::Type<T> &",64 [](PrintingPolicy &Policy) { Policy.FullyQualifiedName = true; }));65}66 67TEST(TypePrinter, TemplateId2) {68 std::string Code = R"cpp(69 template <template <typename ...> class TemplatedType>70 void func(TemplatedType<int> Param);71 )cpp";72 auto Matcher = parmVarDecl(hasType(qualType().bind("id")));73 74 // Regression test ensuring we do not segfault getting the QualType as a75 // string.76 ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher, "<int>",77 [](PrintingPolicy &Policy) {78 Policy.FullyQualifiedName = true;79 Policy.PrintAsCanonical = true;80 }));81}82 83TEST(TypePrinter, ParamsUglified) {84 llvm::StringLiteral Code = R"cpp(85 template <typename _Tp, template <typename> class __f>86 const __f<_Tp&> *A = nullptr;87 )cpp";88 auto Clean = [](PrintingPolicy &Policy) {89 Policy.CleanUglifiedParameters = true;90 };91 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));98}99 100TEST(TypePrinter, TemplateSpecializationFullyQualified) {101 llvm::StringLiteral Code = R"cpp(102 namespace shared {103 namespace a {104 template <typename T>105 struct S {};106 } // namespace a107 namespace b {108 struct Foo {};109 } // namespace b110 using Alias = a::S<b::Foo>;111 } // namespace shared112 )cpp";113 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 = false; }));119 ASSERT_TRUE(PrintedTypeMatches(120 Code, {}, Matcher, "shared::a::S<shared::b::Foo>",121 [](PrintingPolicy &Policy) { Policy.FullyQualifiedName = true; }));122}123 124TEST(TypePrinter, TemplateIdWithNTTP) {125 constexpr char Code[] = R"cpp(126 template <int N>127 struct Str {128 constexpr Str(char const (&s)[N]) { __builtin_memcpy(value, s, N); }129 char value[N];130 };131 template <Str> class ASCII {};132 133 ASCII<"this nontype template argument is too long to print"> x;134 )cpp";135 auto Matcher = classTemplateSpecializationDecl(136 hasName("ASCII"), has(cxxConstructorDecl(137 isMoveConstructor(),138 has(parmVarDecl(hasType(qualType().bind("id")))))));139 140 ASSERT_TRUE(PrintedTypeMatches(141 Code, {"-std=c++20"}, Matcher,142 R"(ASCII<Str<52>{"this nontype template argument is [...]"}> &&)",143 [](PrintingPolicy &Policy) {144 Policy.EntireContentsOfLargeArray = false;145 }));146 147 ASSERT_TRUE(PrintedTypeMatches(148 Code, {"-std=c++20"}, Matcher,149 R"(ASCII<Str<52>{"this nontype template argument is too long to print"}> &&)",150 [](PrintingPolicy &Policy) {151 Policy.EntireContentsOfLargeArray = true;152 }));153}154 155TEST(TypePrinter, TemplateArgumentsSubstitution) {156 constexpr char Code[] = R"cpp(157 template <typename Y> class X {};158 typedef X<int> A;159 int foo() {160 return sizeof(A);161 }162 )cpp";163 auto Matcher = typedefNameDecl(hasName("A"), hasType(qualType().bind("id")));164 ASSERT_TRUE(PrintedTypeMatches(Code, {}, Matcher, "X<int>",165 [](PrintingPolicy &Policy) {166 Policy.SuppressTagKeyword = false;167 Policy.SuppressScope = true;168 }));169}170 171TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {172 /// Tests clang::isSubstitutedDefaultArgument on TemplateArguments173 /// that are of kind TemplateArgument::Expression174 constexpr char Code[] = R"cpp(175 constexpr bool func() { return true; }176 177 template <typename T1 = int,178 int T2 = 42,179 T1 T3 = 43,180 int T4 = sizeof(T1),181 bool T5 = func()182 >183 struct Foo {184 };185 186 Foo<int, 40 + 2> X;187 )cpp";188 189 auto AST = tooling::buildASTFromCodeWithArgs(Code, /*Args=*/{"-std=c++20"});190 ASTContext &Ctx = AST->getASTContext();191 192 auto const *CTD = selectFirst<ClassTemplateDecl>(193 "id", match(classTemplateDecl(hasName("Foo")).bind("id"), Ctx));194 ASSERT_NE(CTD, nullptr);195 auto const *CTSD = *CTD->specializations().begin();196 ASSERT_NE(CTSD, nullptr);197 auto const *Params = CTD->getTemplateParameters();198 ASSERT_NE(Params, nullptr);199 auto const &ArgList = CTSD->getTemplateArgs();200 201 auto createBinOpExpr = [&](uint32_t LHS, uint32_t RHS,202 uint32_t Result) -> ConstantExpr * {203 const int numBits = 32;204 clang::APValue ResultVal{llvm::APSInt(llvm::APInt(numBits, Result))};205 auto *LHSInt = IntegerLiteral::Create(Ctx, llvm::APInt(numBits, LHS),206 Ctx.UnsignedIntTy, {});207 auto *RHSInt = IntegerLiteral::Create(Ctx, llvm::APInt(numBits, RHS),208 Ctx.UnsignedIntTy, {});209 auto *BinOp = BinaryOperator::Create(210 Ctx, LHSInt, RHSInt, BinaryOperatorKind::BO_Add, Ctx.UnsignedIntTy,211 ExprValueKind::VK_PRValue, ExprObjectKind::OK_Ordinary, {}, {});212 return ConstantExpr::Create(Ctx, dyn_cast<Expr>(BinOp), ResultVal);213 };214 215 {216 // Arg is an integral '42'217 auto const &Arg = ArgList.get(1);218 ASSERT_EQ(Arg.getKind(), TemplateArgument::Integral);219 220 // Param has default expr which evaluates to '42'221 auto const *Param = Params->getParam(1);222 223 EXPECT_TRUE(clang::isSubstitutedDefaultArgument(224 Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));225 }226 227 {228 // Arg is an integral '41'229 llvm::APInt Int(32, 41);230 TemplateArgument Arg(Ctx, llvm::APSInt(Int), Ctx.UnsignedIntTy);231 232 // Param has default expr which evaluates to '42'233 auto const *Param = Params->getParam(1);234 235 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(236 Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));237 }238 239 {240 // Arg is an integral '4'241 llvm::APInt Int(32, 4);242 TemplateArgument Arg(Ctx, llvm::APSInt(Int), Ctx.UnsignedIntTy);243 244 // Param has is value-dependent expression (i.e., sizeof(T))245 auto const *Param = Params->getParam(3);246 247 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(248 Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));249 }250 251 {252 const int LHS = 40;253 const int RHS = 2;254 const int Result = 42;255 auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);256 // Arg is instantiated with '40 + 2'257 TemplateArgument Arg(ConstExpr, /*IsCanonical=*/false);258 259 // Param has default expr of '42'260 auto const *Param = Params->getParam(1);261 262 EXPECT_TRUE(clang::isSubstitutedDefaultArgument(263 Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));264 }265 266 {267 const int LHS = 40;268 const int RHS = 1;269 const int Result = 41;270 auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);271 272 // Arg is instantiated with '40 + 1'273 TemplateArgument Arg(ConstExpr, /*IsCanonical=*/false);274 275 // Param has default expr of '42'276 auto const *Param = Params->getParam(1);277 278 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(279 Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));280 }281 282 {283 const int LHS = 4;284 const int RHS = 0;285 const int Result = 4;286 auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);287 288 // Arg is instantiated with '4 + 0'289 TemplateArgument Arg(ConstExpr, /*IsCanonical=*/false);290 291 // Param has is value-dependent expression (i.e., sizeof(T))292 auto const *Param = Params->getParam(3);293 294 EXPECT_FALSE(clang::isSubstitutedDefaultArgument(295 Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));296 }297}298 299TEST(TypePrinter, NestedNameSpecifiers) {300 constexpr char Code[] = R"cpp(301 void level1() {302 struct Inner {303 Inner(int) {304 struct {305 union {} u;306 } imem;307 }308 };309 }310 )cpp";311 312 // Types scoped immediately inside a function don't print the function name in313 // their scope.314 ASSERT_TRUE(PrintedTypeMatches(315 Code, {}, varDecl(hasName("imem"), hasType(qualType().bind("id"))),316 "struct (unnamed)", [](PrintingPolicy &Policy) {317 Policy.FullyQualifiedName = true;318 Policy.AnonymousTagLocations = false;319 }));320 321 ASSERT_TRUE(PrintedTypeMatches(322 Code, {}, varDecl(hasName("imem"), hasType(qualType().bind("id"))),323 "struct (unnamed)", [](PrintingPolicy &Policy) {324 Policy.FullyQualifiedName = false;325 Policy.AnonymousTagLocations = false;326 }));327 328 // Further levels of nesting print the entire scope.329 ASSERT_TRUE(PrintedTypeMatches(330 Code, {}, fieldDecl(hasName("u"), hasType(qualType().bind("id"))),331 "union level1()::Inner::Inner(int)::(anonymous struct)::(unnamed)",332 [](PrintingPolicy &Policy) {333 Policy.FullyQualifiedName = true;334 Policy.AnonymousTagLocations = false;335 }));336 337 ASSERT_TRUE(PrintedTypeMatches(338 Code, {}, fieldDecl(hasName("u"), hasType(qualType().bind("id"))),339 "union (unnamed)", [](PrintingPolicy &Policy) {340 Policy.FullyQualifiedName = false;341 Policy.AnonymousTagLocations = false;342 }));343}344 345TEST(TypePrinter, NestedNameSpecifiersTypedef) {346 constexpr char Code[] = R"cpp(347 typedef union {348 struct {349 struct {350 unsigned int baz;351 } bar;352 };353 } foo;354 )cpp";355 356 ASSERT_TRUE(PrintedTypeMatches(357 Code, {}, fieldDecl(hasName("bar"), hasType(qualType().bind("id"))),358 "struct foo::(anonymous struct)::(unnamed)", [](PrintingPolicy &Policy) {359 Policy.FullyQualifiedName = true;360 Policy.AnonymousTagLocations = false;361 }));362}363