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
28 using namespace clang;
29 using namespace ast_matchers;
30 using namespace tooling;
31
32 namespace {
33
34 enum class StdVer { CXX98, CXX11, CXX14, CXX17, CXX2a };
35
FunctionBodyMatcher(StringRef ContainingFunction)36 DeclarationMatcher FunctionBodyMatcher(StringRef ContainingFunction) {
37 return functionDecl(hasName(ContainingFunction),
38 has(compoundStmt(has(stmt().bind("id")))));
39 }
40
PrintStmt(raw_ostream & Out,const ASTContext * Context,const Stmt * S,PrintingPolicyAdjuster PolicyAdjuster)41 static 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(Out, /*Helper*/ nullptr, Policy);
48 }
49
50 template <typename Matcher>
51 ::testing::AssertionResult
PrintedStmtMatches(StringRef Code,const std::vector<std::string> & Args,const Matcher & NodeMatch,StringRef ExpectedPrinted,PrintingPolicyAdjuster PolicyAdjuster=nullptr)52 PrintedStmtMatches(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
59 template <typename T>
60 ::testing::AssertionResult
PrintedStmtCXXMatches(StdVer Standard,StringRef Code,const T & NodeMatch,StringRef ExpectedPrinted,PrintingPolicyAdjuster PolicyAdjuster=nullptr)61 PrintedStmtCXXMatches(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::CXX2a: StdOpt = "-std=c++2a"; break;
71 }
72
73 std::vector<std::string> Args = {
74 StdOpt,
75 "-Wno-unused-value",
76 };
77 return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted,
78 PolicyAdjuster);
79 }
80
81 template <typename T>
82 ::testing::AssertionResult
PrintedStmtMSMatches(StringRef Code,const T & NodeMatch,StringRef ExpectedPrinted,PrintingPolicyAdjuster PolicyAdjuster=nullptr)83 PrintedStmtMSMatches(StringRef Code, const T &NodeMatch,
84 StringRef ExpectedPrinted,
85 PrintingPolicyAdjuster PolicyAdjuster = nullptr) {
86 std::vector<std::string> Args = {
87 "-std=c++98",
88 "-target", "i686-pc-win32",
89 "-fms-extensions",
90 "-Wno-unused-value",
91 };
92 return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted,
93 PolicyAdjuster);
94 }
95
96 template <typename T>
97 ::testing::AssertionResult
PrintedStmtObjCMatches(StringRef Code,const T & NodeMatch,StringRef ExpectedPrinted,PrintingPolicyAdjuster PolicyAdjuster=nullptr)98 PrintedStmtObjCMatches(StringRef Code, const T &NodeMatch,
99 StringRef ExpectedPrinted,
100 PrintingPolicyAdjuster PolicyAdjuster = nullptr) {
101 std::vector<std::string> Args = {
102 "-ObjC",
103 "-fobjc-runtime=macosx-10.12.0",
104 };
105 return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted,
106 PolicyAdjuster);
107 }
108
109 } // unnamed namespace
110
TEST(StmtPrinter,TestIntegerLiteral)111 TEST(StmtPrinter, TestIntegerLiteral) {
112 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX98,
113 "void A() {"
114 " 1, -1, 1U, 1u,"
115 " 1L, 1l, -1L, 1UL, 1ul,"
116 " 1LL, -1LL, 1ULL;"
117 "}",
118 FunctionBodyMatcher("A"),
119 "1 , -1 , 1U , 1U , "
120 "1L , 1L , -1L , 1UL , 1UL , "
121 "1LL , -1LL , 1ULL"));
122 // Should be: with semicolon
123 }
124
TEST(StmtPrinter,TestMSIntegerLiteral)125 TEST(StmtPrinter, TestMSIntegerLiteral) {
126 ASSERT_TRUE(PrintedStmtMSMatches(
127 "void A() {"
128 " 1i8, -1i8, 1ui8, "
129 " 1i16, -1i16, 1ui16, "
130 " 1i32, -1i32, 1ui32, "
131 " 1i64, -1i64, 1ui64;"
132 "}",
133 FunctionBodyMatcher("A"),
134 "1i8 , -1i8 , 1Ui8 , "
135 "1i16 , -1i16 , 1Ui16 , "
136 "1 , -1 , 1U , "
137 "1LL , -1LL , 1ULL"));
138 // Should be: with semicolon
139 }
140
TEST(StmtPrinter,TestFloatingPointLiteral)141 TEST(StmtPrinter, TestFloatingPointLiteral) {
142 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX98,
143 "void A() { 1.0f, -1.0f, 1.0, -1.0, 1.0l, -1.0l; }",
144 FunctionBodyMatcher("A"),
145 "1.F , -1.F , 1. , -1. , 1.L , -1.L"));
146 // Should be: with semicolon
147 }
148
TEST(StmtPrinter,TestCXXConversionDeclImplicit)149 TEST(StmtPrinter, TestCXXConversionDeclImplicit) {
150 ASSERT_TRUE(PrintedStmtCXXMatches(
151 StdVer::CXX98,
152 "struct A {"
153 "operator void *();"
154 "A operator&(A);"
155 "};"
156 "void bar(void *);"
157 "void foo(A a, A b) {"
158 " bar(a & b);"
159 "}",
160 traverse(TK_AsIs, cxxMemberCallExpr(anything()).bind("id")), "a & b"));
161 }
162
TEST(StmtPrinter,TestCXXConversionDeclExplicit)163 TEST(StmtPrinter, TestCXXConversionDeclExplicit) {
164 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
165 "struct A {"
166 "operator void *();"
167 "A operator&(A);"
168 "};"
169 "void bar(void *);"
170 "void foo(A a, A b) {"
171 " auto x = (a & b).operator void *();"
172 "}",
173 cxxMemberCallExpr(anything()).bind("id"),
174 "(a & b)"));
175 // WRONG; Should be: (a & b).operator void *()
176 }
177
TEST(StmtPrinter,TestCXXLamda)178 TEST(StmtPrinter, TestCXXLamda) {
179 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
180 "void A() {"
181 " auto l = [] { };"
182 "}",
183 lambdaExpr(anything()).bind("id"),
184 "[] {\n"
185 "}"));
186
187 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
188 "void A() {"
189 " int a = 0, b = 1;"
190 " auto l = [a,b](int c, float d) { };"
191 "}",
192 lambdaExpr(anything()).bind("id"),
193 "[a, b](int c, float d) {\n"
194 "}"));
195
196 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX14,
197 "void A() {"
198 " auto l = [](auto a, int b, auto c, int, auto) { };"
199 "}",
200 lambdaExpr(anything()).bind("id"),
201 "[](auto a, int b, auto c, int, auto) {\n"
202 "}"));
203
204 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX2a,
205 "void A() {"
206 " auto l = []<typename T1, class T2, int I,"
207 " template<class, typename> class T3>"
208 " (int a, auto, int, auto d) { };"
209 "}",
210 lambdaExpr(anything()).bind("id"),
211 "[]<typename T1, class T2, int I, template <class, typename> class T3>(int a, auto, int, auto d) {\n"
212 "}"));
213 }
214
TEST(StmtPrinter,TestNoImplicitBases)215 TEST(StmtPrinter, TestNoImplicitBases) {
216 const char *CPPSource = R"(
217 class A {
218 int field;
219 int member() { return field; }
220 };
221 )";
222 // No implicit 'this'.
223 ASSERT_TRUE(PrintedStmtCXXMatches(
224 StdVer::CXX11, CPPSource, memberExpr(anything()).bind("id"), "field",
225
226 [](PrintingPolicy &PP) { PP.SuppressImplicitBase = true; }));
227 // Print implicit 'this'.
228 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
229 CPPSource, memberExpr(anything()).bind("id"), "this->field"));
230
231 const char *ObjCSource = R"(
232 @interface I {
233 int ivar;
234 }
235 @end
236 @implementation I
237 - (int) method {
238 return ivar;
239 }
240 @end
241 )";
242 // No implicit 'self'.
243 ASSERT_TRUE(PrintedStmtObjCMatches(
244 ObjCSource, returnStmt().bind("id"), "return ivar;\n",
245
246 [](PrintingPolicy &PP) { PP.SuppressImplicitBase = true; }));
247 // Print implicit 'self'.
248 ASSERT_TRUE(PrintedStmtObjCMatches(ObjCSource, returnStmt().bind("id"),
249 "return self->ivar;\n"));
250 }
251
TEST(StmtPrinter,TerseOutputWithLambdas)252 TEST(StmtPrinter, TerseOutputWithLambdas) {
253 const char *CPPSource = "auto lamb = []{ return 0; };";
254
255 // body is printed when TerseOutput is off(default).
256 ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11, CPPSource,
257 lambdaExpr(anything()).bind("id"),
258 "[] {\n return 0;\n}"));
259
260 // body not printed when TerseOutput is on.
261 ASSERT_TRUE(PrintedStmtCXXMatches(
262 StdVer::CXX11, CPPSource, lambdaExpr(anything()).bind("id"), "[] {}",
263
264 [](PrintingPolicy &PP) { PP.TerseOutput = true; }));
265 }
266