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 
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
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
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
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 
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 
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 
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 
131 TEST(StmtPrinter, TestCXXConversionDeclImplicit) {
132   ASSERT_TRUE(
133       PrintedStmtCXXMatches(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(ast_type_traits::TK_AsIs,
143                                      cxxMemberCallExpr(anything()).bind("id")),
144                             "a & b"));
145 }
146 
147 TEST(StmtPrinter, TestCXXConversionDeclExplicit) {
148   ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
149     "struct A {"
150       "operator void *();"
151       "A operator&(A);"
152     "};"
153     "void bar(void *);"
154     "void foo(A a, A b) {"
155     "  auto x = (a & b).operator void *();"
156     "}",
157     cxxMemberCallExpr(anything()).bind("id"),
158     "(a & b)"));
159     // WRONG; Should be: (a & b).operator void *()
160 }
161 
162 TEST(StmtPrinter, TestCXXLamda) {
163   ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
164     "void A() {"
165     "  auto l = [] { };"
166     "}",
167     lambdaExpr(anything()).bind("id"),
168     "[] {\n"
169     "}"));
170 
171   ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
172     "void A() {"
173     "  int a = 0, b = 1;"
174     "  auto l = [a,b](int c, float d) { };"
175     "}",
176     lambdaExpr(anything()).bind("id"),
177     "[a, b](int c, float d) {\n"
178     "}"));
179 
180   ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX14,
181     "void A() {"
182     "  auto l = [](auto a, int b, auto c, int, auto) { };"
183     "}",
184     lambdaExpr(anything()).bind("id"),
185     "[](auto a, int b, auto c, int, auto) {\n"
186     "}"));
187 
188   ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX2a,
189     "void A() {"
190     "  auto l = []<typename T1, class T2, int I,"
191     "              template<class, typename> class T3>"
192     "           (int a, auto, int, auto d) { };"
193     "}",
194     lambdaExpr(anything()).bind("id"),
195     "[]<typename T1, class T2, int I, template <class, typename> class T3>(int a, auto, int, auto d) {\n"
196     "}"));
197 }
198 
199 TEST(StmtPrinter, TestNoImplicitBases) {
200   const char *CPPSource = R"(
201 class A {
202   int field;
203   int member() { return field; }
204 };
205 )";
206   // No implicit 'this'.
207   ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
208       CPPSource, memberExpr(anything()).bind("id"), "field",
209       PolicyAdjusterType(
210           [](PrintingPolicy &PP) { PP.SuppressImplicitBase = true; })));
211   // Print implicit 'this'.
212   ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11,
213       CPPSource, memberExpr(anything()).bind("id"), "this->field"));
214 
215   const char *ObjCSource = R"(
216 @interface I {
217    int ivar;
218 }
219 @end
220 @implementation I
221 - (int) method {
222   return ivar;
223 }
224 @end
225       )";
226   // No implicit 'self'.
227   ASSERT_TRUE(PrintedStmtObjCMatches(ObjCSource, returnStmt().bind("id"),
228                                      "return ivar;\n",
229                                      PolicyAdjusterType([](PrintingPolicy &PP) {
230                                        PP.SuppressImplicitBase = true;
231                                      })));
232   // Print implicit 'self'.
233   ASSERT_TRUE(PrintedStmtObjCMatches(ObjCSource, returnStmt().bind("id"),
234                                      "return self->ivar;\n"));
235 }
236 
237 TEST(StmtPrinter, TerseOutputWithLambdas) {
238   const char *CPPSource = "auto lamb = []{ return 0; };";
239 
240   // body is printed when TerseOutput is off(default).
241   ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11, CPPSource,
242                                     lambdaExpr(anything()).bind("id"),
243                                     "[] {\n    return 0;\n}"));
244 
245   // body not printed when TerseOutput is on.
246   ASSERT_TRUE(PrintedStmtCXXMatches(
247       StdVer::CXX11, CPPSource, lambdaExpr(anything()).bind("id"), "[] {}",
248       PolicyAdjusterType([](PrintingPolicy &PP) { PP.TerseOutput = true; })));
249 }
250