1//===- unittests/AST/StmtPrinterTest.cpp --- Statement 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 Stmt::printPretty() and related methods.
10//
11// Search this file for WRONG to see test cases that are producing something
12// completely wrong, invalid C++ or just misleading.
13//
14// These tests have a coding convention:
15// * statements to be printed should be contained within a function named 'A'
16// unless it should have some special name (e.g., 'operator+');
17// * additional helper declarations are 'Z', 'Y', 'X' and so on.
18//
19//===----------------------------------------------------------------------===//
20
21#include "ASTPrint.h"
22#include "clang/AST/ASTContext.h"
23#include "clang/ASTMatchers/ASTMatchFinder.h"
24#include "clang/Tooling/Tooling.h"
25#include "llvm/ADT/SmallString.h"
26#include "gtest/gtest.h"
27
28using namespace clang;
29using namespace ast_matchers;
30using namespace tooling;
31
32namespace {
33
34enum class StdVer { CXX98, CXX11, CXX14, CXX17, CXX20 };
35
36DeclarationMatcher FunctionBodyMatcher(StringRef ContainingFunction) {
37 return functionDecl(hasName(Name: ContainingFunction),
38 has(compoundStmt(has(stmt().bind(ID: "id")))));
39}
40
41static void PrintStmt(raw_ostream &Out, const ASTContext *Context,
42 const Stmt *S, PrintingPolicyAdjuster PolicyAdjuster) {
43 assert(S != nullptr && "Expected non-null Stmt");
44 PrintingPolicy Policy = Context->getPrintingPolicy();
45 if (PolicyAdjuster)
46 PolicyAdjuster(Policy);
47 S->printPretty(OS&: Out, /*Helper*/ nullptr, Policy);
48}
49
50template <typename Matcher>
51::testing::AssertionResult
52PrintedStmtMatches(StringRef Code, const std::vector<std::string> &Args,
53 const Matcher &NodeMatch, StringRef ExpectedPrinted,
54 PrintingPolicyAdjuster PolicyAdjuster = nullptr) {
55 return PrintedNodeMatches<Stmt>(Code, Args, NodeMatch, ExpectedPrinted, "",
56 PrintStmt, PolicyAdjuster);
57}
58
59template <typename T>
60::testing::AssertionResult
61PrintedStmtCXXMatches(StdVer Standard, StringRef Code, const T &NodeMatch,
62 StringRef ExpectedPrinted,
63 PrintingPolicyAdjuster PolicyAdjuster = nullptr) {
64 const char *StdOpt;
65 switch (Standard) {
66 case StdVer::CXX98: StdOpt = "-std=c++98"; break;
67 case StdVer::CXX11: StdOpt = "-std=c++11"; break;
68 case StdVer::CXX14: StdOpt = "-std=c++14"; break;
69 case StdVer::CXX17: StdOpt = "-std=c++17"; break;
70 case StdVer::CXX20:
71 StdOpt = "-std=c++20";
72 break;
73 }
74
75 std::vector<std::string> Args = {
76 StdOpt,
77 "-Wno-unused-value",
78 };
79 return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted,
80 PolicyAdjuster);
81}
82
83template <typename T>
84::testing::AssertionResult
85PrintedStmtMSMatches(StringRef Code, const T &NodeMatch,
86 StringRef ExpectedPrinted,
87 PrintingPolicyAdjuster PolicyAdjuster = nullptr) {
88 std::vector<std::string> Args = {
89 "-std=c++98",
90 "-target", "i686-pc-win32",
91 "-fms-extensions",
92 "-Wno-unused-value",
93 };
94 return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted,
95 PolicyAdjuster);
96}
97
98template <typename T>
99::testing::AssertionResult
100PrintedStmtObjCMatches(StringRef Code, const T &NodeMatch,
101 StringRef ExpectedPrinted,
102 PrintingPolicyAdjuster PolicyAdjuster = nullptr) {
103 std::vector<std::string> Args = {
104 "-ObjC",
105 "-fobjc-runtime=macosx-10.12.0",
106 };
107 return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted,
108 PolicyAdjuster);
109}
110
111} // unnamed namespace
112
113TEST(StmtPrinter, TestIntegerLiteral) {
114 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX98,
115 "void A() {"
116 " 1, -1, 1U, 1u,"
117 " 1L, 1l, -1L, 1UL, 1ul,"
118 " 1LL, -1LL, 1ULL;"
119 "}",
120 FunctionBodyMatcher("A"),
121 "1 , -1 , 1U , 1U , "
122 "1L , 1L , -1L , 1UL , 1UL , "
123 "1LL , -1LL , 1ULL"));
124 // Should be: with semicolon
125}
126
127TEST(StmtPrinter, TestMSIntegerLiteral) {
128 ASSERT_TRUE(PrintedStmtMSMatches(
129 "void A() {"
130 " 1i8, -1i8, 1ui8, "
131 " 1i16, -1i16, 1ui16, "
132 " 1i32, -1i32, 1ui32, "
133 " 1i64, -1i64, 1ui64;"
134 "}",
135 FunctionBodyMatcher("A"),
136 "1i8 , -1i8 , 1Ui8 , "
137 "1i16 , -1i16 , 1Ui16 , "
138 "1 , -1 , 1U , "
139 "1LL , -1LL , 1ULL"));
140 // Should be: with semicolon
141}
142
143TEST(StmtPrinter, TestFloatingPointLiteral) {
144 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX98,
145 "void A() { 1.0f, -1.0f, 1.0, -1.0, 1.0l, -1.0l; }",
146 FunctionBodyMatcher("A"),
147 "1.F , -1.F , 1. , -1. , 1.L , -1.L"));
148 // Should be: with semicolon
149}
150
151TEST(StmtPrinter, TestStringLiteralOperatorTemplate_Pack) {
152 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
153 R"cpp(
154 template <char...> constexpr double operator""_c() { return 42; }
155 void A() {
156 constexpr auto waldo = 42_c;
157 }
158)cpp",
159 FunctionBodyMatcher("A"),
160 "constexpr auto waldo = 42_c;\n"));
161}
162
163TEST(StmtPrinter, TestStringLiteralOperatorTemplate_Class) {
164 ASSERT_TRUE(PrintedStmtCXXMatches(
165 StdVer::CXX20,
166 R"cpp(
167 struct C {
168 template <unsigned N> constexpr C(const char (&)[N]) : n(N) {}
169 unsigned n;
170 };
171 template <C c> constexpr auto operator""_c() { return c.n; }
172 void A() {
173 constexpr auto waldo = "abc"_c;
174 }
175)cpp",
176 FunctionBodyMatcher("A"),
177 "constexpr auto waldo = operator\"\"_c<C{4}>();\n"));
178}
179
180TEST(StmtPrinter, TestCXXConversionDeclImplicit) {
181 ASSERT_TRUE(PrintedStmtCXXMatches(
182 StdVer::CXX98,
183 "struct A {"
184 "operator void *();"
185 "A operator&(A);"
186 "};"
187 "void bar(void *);"
188 "void foo(A a, A b) {"
189 " bar(a & b);"
190 "}",
191 traverse(TK_AsIs, cxxMemberCallExpr(anything()).bind("id")), "a & b"));
192}
193
194TEST(StmtPrinter, TestCXXConversionDeclExplicit) {
195 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
196 "struct A {"
197 "operator void *();"
198 "A operator&(A);"
199 "};"
200 "void bar(void *);"
201 "void foo(A a, A b) {"
202 " auto x = (a & b).operator void *();"
203 "}",
204 cxxMemberCallExpr(anything()).bind("id"),
205 "(a & b)"));
206 // WRONG; Should be: (a & b).operator void *()
207}
208
209TEST(StmtPrinter, TestCXXLamda) {
210 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
211 "void A() {"
212 " auto l = [] { };"
213 "}",
214 lambdaExpr(anything()).bind("id"),
215 "[] {\n"
216 "}"));
217
218 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
219 "void A() {"
220 " int a = 0, b = 1;"
221 " auto l = [a,b](int c, float d) { };"
222 "}",
223 lambdaExpr(anything()).bind("id"),
224 "[a, b](int c, float d) {\n"
225 "}"));
226
227 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX14,
228 "void A() {"
229 " auto l = [](auto a, int b, auto c, int, auto) { };"
230 "}",
231 lambdaExpr(anything()).bind("id"),
232 "[](auto a, int b, auto c, int, auto) {\n"
233 "}"));
234
235 ASSERT_TRUE(
236 PrintedStmtCXXMatches(StdVer::CXX20,
237 "void A() {"
238 " auto l = []<typename T1, class T2, int I,"
239 " template<class, typename> class T3>"
240 " (int a, auto, int, auto d) { };"
241 "}",
242 lambdaExpr(anything()).bind("id"),
243 "[]<typename T1, class T2, int I, template <class, "
244 "typename> class T3>(int a, auto, int, auto d) {\n"
245 "}"));
246}
247
248TEST(StmtPrinter, TestNoImplicitBases) {
249 const char *CPPSource = R"(
250class A {
251 int field;
252 int member() { return field; }
253};
254)";
255 // No implicit 'this'.
256 ASSERT_TRUE(PrintedStmtCXXMatches(
257 StdVer::CXX11, CPPSource, memberExpr(anything()).bind("id"), "field",
258
259 [](PrintingPolicy &PP) { PP.SuppressImplicitBase = true; }));
260 // Print implicit 'this'.
261 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
262 CPPSource, memberExpr(anything()).bind("id"), "this->field"));
263
264 const char *ObjCSource = R"(
265@interface I {
266 int ivar;
267}
268@end
269@implementation I
270- (int) method {
271 return ivar;
272}
273@end
274 )";
275 // No implicit 'self'.
276 ASSERT_TRUE(PrintedStmtObjCMatches(
277 ObjCSource, returnStmt().bind("id"), "return ivar;\n",
278
279 [](PrintingPolicy &PP) { PP.SuppressImplicitBase = true; }));
280 // Print implicit 'self'.
281 ASSERT_TRUE(PrintedStmtObjCMatches(ObjCSource, returnStmt().bind("id"),
282 "return self->ivar;\n"));
283}
284
285TEST(StmtPrinter, TerseOutputWithLambdas) {
286 const char *CPPSource = "auto lamb = []{ return 0; };";
287
288 // body is printed when TerseOutput is off(default).
289 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11, CPPSource,
290 lambdaExpr(anything()).bind("id"),
291 "[] {\n return 0;\n}"));
292
293 // body not printed when TerseOutput is on.
294 ASSERT_TRUE(PrintedStmtCXXMatches(
295 StdVer::CXX11, CPPSource, lambdaExpr(anything()).bind("id"), "[] {}",
296
297 [](PrintingPolicy &PP) { PP.TerseOutput = true; }));
298}
299
300TEST(StmtPrinter, ParamsUglified) {
301 llvm::StringLiteral Code = R"cpp(
302 template <typename _T, int _I, template <typename> class _C>
303 auto foo(int __j) {
304 return typename _C<_T>::_F(_I, __j);
305 }
306 )cpp";
307 auto Clean = [](PrintingPolicy &Policy) {
308 Policy.CleanUglifiedParameters = true;
309 };
310
311 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX14, Code,
312 returnStmt().bind("id"),
313 "return typename _C<_T>::_F(_I, __j);\n"));
314 ASSERT_TRUE(
315 PrintedStmtCXXMatches(StdVer::CXX14, Code, returnStmt().bind("id"),
316 "return typename C<T>::_F(I, j);\n", Clean));
317}
318

source code of clang/unittests/AST/StmtPrinterTest.cpp