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