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 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 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::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 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 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 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 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 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 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 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 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 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 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