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, CXX20 }; |
35 | |
36 | DeclarationMatcher FunctionBodyMatcher(StringRef ContainingFunction) { |
37 | return functionDecl(hasName(Name: ContainingFunction), |
38 | has(compoundStmt(has(stmt().bind(ID: "id" ))))); |
39 | } |
40 | |
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(OS&: Out, /*Helper*/ nullptr, Policy); |
48 | } |
49 | |
50 | template <typename Matcher> |
51 | ::testing::AssertionResult |
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 |
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::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 | |
83 | template <typename T> |
84 | ::testing::AssertionResult |
85 | PrintedStmtMSMatches(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 | |
98 | template <typename T> |
99 | ::testing::AssertionResult |
100 | PrintedStmtObjCMatches(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 | |
113 | TEST(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 | |
127 | TEST(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 | |
143 | TEST(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 | |
151 | TEST(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 | |
163 | TEST(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 | |
180 | TEST(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 | |
194 | TEST(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 | |
209 | TEST(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 | |
248 | TEST(StmtPrinter, TestNoImplicitBases) { |
249 | const char *CPPSource = R"( |
250 | class 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 | |
285 | TEST(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 | |
300 | TEST(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 | |