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