1 //===- unittest/Tooling/SourceCodeBuildersTest.cpp ------------------------===// 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 #include "clang/Tooling/Transformer/SourceCodeBuilders.h" 10 #include "clang/ASTMatchers/ASTMatchFinder.h" 11 #include "clang/ASTMatchers/ASTMatchers.h" 12 #include "clang/Tooling/Tooling.h" 13 #include "llvm/Testing/Support/SupportHelpers.h" 14 #include "gmock/gmock.h" 15 #include "gtest/gtest.h" 16 17 using namespace clang; 18 using namespace tooling; 19 using namespace ast_matchers; 20 21 namespace { 22 using MatchResult = MatchFinder::MatchResult; 23 using llvm::ValueIs; 24 25 // Create a valid translation unit from a statement. 26 static std::string wrapSnippet(StringRef StatementCode) { 27 return ("struct S { S(); S(int); int field; };\n" 28 "S operator+(const S &a, const S &b);\n" 29 "auto test_snippet = []{" + 30 StatementCode + "};") 31 .str(); 32 } 33 34 static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) { 35 return varDecl(hasName("test_snippet"), 36 hasDescendant(compoundStmt(hasAnySubstatement(Matcher)))); 37 } 38 39 struct TestMatch { 40 // The AST unit from which `result` is built. We bundle it because it backs 41 // the result. Users are not expected to access it. 42 std::unique_ptr<ASTUnit> AstUnit; 43 // The result to use in the test. References `ast_unit`. 44 MatchResult Result; 45 }; 46 47 // Matches `Matcher` against the statement `StatementCode` and returns the 48 // result. Handles putting the statement inside a function and modifying the 49 // matcher correspondingly. `Matcher` should match one of the statements in 50 // `StatementCode` exactly -- that is, produce exactly one match. However, 51 // `StatementCode` may contain other statements not described by `Matcher`. 52 static llvm::Optional<TestMatch> matchStmt(StringRef StatementCode, 53 StatementMatcher Matcher) { 54 auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode)); 55 if (AstUnit == nullptr) { 56 ADD_FAILURE() << "AST construction failed"; 57 return llvm::None; 58 } 59 ASTContext &Context = AstUnit->getASTContext(); 60 auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context); 61 // We expect a single, exact match for the statement. 62 if (Matches.size() != 1) { 63 ADD_FAILURE() << "Wrong number of matches: " << Matches.size(); 64 return llvm::None; 65 } 66 return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)}; 67 } 68 69 static void testPredicate(bool (*Pred)(const Expr &), StringRef Snippet, 70 bool Expected) { 71 auto StmtMatch = matchStmt(Snippet, expr().bind("expr")); 72 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; 73 EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"))) 74 << "Snippet: " << Snippet; 75 } 76 77 // Tests the predicate on the call argument, assuming `Snippet` is a function 78 // call. 79 static void testPredicateOnArg(bool (*Pred)(const Expr &), StringRef Snippet, 80 bool Expected) { 81 auto StmtMatch = matchStmt( 82 Snippet, expr(ignoringImplicit(callExpr(hasArgument( 83 0, ignoringElidableConstructorCall(expr().bind("arg"))))))); 84 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; 85 EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("arg"))) 86 << "Snippet: " << Snippet; 87 } 88 89 TEST(SourceCodeBuildersTest, needParensAfterUnaryOperator) { 90 testPredicate(needParensAfterUnaryOperator, "3 + 5;", true); 91 testPredicate(needParensAfterUnaryOperator, "true ? 3 : 5;", true); 92 testPredicate(needParensAfterUnaryOperator, "S(3) + S(5);", true); 93 94 testPredicate(needParensAfterUnaryOperator, "int x; x;", false); 95 testPredicate(needParensAfterUnaryOperator, "int(3.0);", false); 96 testPredicate(needParensAfterUnaryOperator, "void f(); f();", false); 97 testPredicate(needParensAfterUnaryOperator, "int a[3]; a[0];", false); 98 testPredicate(needParensAfterUnaryOperator, "S x; x.field;", false); 99 testPredicate(needParensAfterUnaryOperator, "int x = 1; --x;", false); 100 testPredicate(needParensAfterUnaryOperator, "int x = 1; -x;", false); 101 } 102 103 TEST(SourceCodeBuildersTest, needParensAfterUnaryOperatorInImplicitConversion) { 104 // The binary operation will be embedded in various implicit 105 // expressions. Verify they are ignored. 106 testPredicateOnArg(needParensAfterUnaryOperator, "void f(S); f(3 + 5);", 107 true); 108 } 109 110 TEST(SourceCodeBuildersTest, mayEverNeedParens) { 111 testPredicate(mayEverNeedParens, "3 + 5;", true); 112 testPredicate(mayEverNeedParens, "true ? 3 : 5;", true); 113 testPredicate(mayEverNeedParens, "int x = 1; --x;", true); 114 testPredicate(mayEverNeedParens, "int x = 1; -x;", true); 115 116 testPredicate(mayEverNeedParens, "int x; x;", false); 117 testPredicate(mayEverNeedParens, "int(3.0);", false); 118 testPredicate(mayEverNeedParens, "void f(); f();", false); 119 testPredicate(mayEverNeedParens, "int a[3]; a[0];", false); 120 testPredicate(mayEverNeedParens, "S x; x.field;", false); 121 } 122 123 TEST(SourceCodeBuildersTest, mayEverNeedParensInImplictConversion) { 124 // The binary operation will be embedded in various implicit 125 // expressions. Verify they are ignored. 126 testPredicateOnArg(mayEverNeedParens, "void f(S); f(3 + 5);", true); 127 } 128 129 static void testBuilder( 130 llvm::Optional<std::string> (*Builder)(const Expr &, const ASTContext &), 131 StringRef Snippet, StringRef Expected) { 132 auto StmtMatch = matchStmt(Snippet, expr().bind("expr")); 133 ASSERT_TRUE(StmtMatch); 134 EXPECT_THAT(Builder(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"), 135 *StmtMatch->Result.Context), 136 ValueIs(std::string(Expected))); 137 } 138 139 TEST(SourceCodeBuildersTest, BuildParensUnaryOp) { 140 testBuilder(buildParens, "-4;", "(-4)"); 141 } 142 143 TEST(SourceCodeBuildersTest, BuildParensBinOp) { 144 testBuilder(buildParens, "4 + 4;", "(4 + 4)"); 145 } 146 147 TEST(SourceCodeBuildersTest, BuildParensValue) { 148 testBuilder(buildParens, "4;", "4"); 149 } 150 151 TEST(SourceCodeBuildersTest, BuildParensSubscript) { 152 testBuilder(buildParens, "int a[3]; a[0];", "a[0]"); 153 } 154 155 TEST(SourceCodeBuildersTest, BuildParensCall) { 156 testBuilder(buildParens, "int f(int); f(4);", "f(4)"); 157 } 158 159 TEST(SourceCodeBuildersTest, BuildAddressOfValue) { 160 testBuilder(buildAddressOf, "S x; x;", "&x"); 161 } 162 163 TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereference) { 164 testBuilder(buildAddressOf, "S *x; *x;", "x"); 165 } 166 167 TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereferenceIgnoresParens) { 168 testBuilder(buildAddressOf, "S *x; *(x);", "x"); 169 } 170 171 TEST(SourceCodeBuildersTest, BuildAddressOfBinaryOperation) { 172 testBuilder(buildAddressOf, "S x; x + x;", "&(x + x)"); 173 } 174 175 TEST(SourceCodeBuildersTest, BuildAddressOfImplicitThis) { 176 StringRef Snippet = R"cc( 177 struct Struct { 178 void foo() {} 179 void bar() { 180 foo(); 181 } 182 }; 183 )cc"; 184 auto StmtMatch = matchStmt( 185 Snippet, 186 cxxMemberCallExpr(onImplicitObjectArgument(cxxThisExpr().bind("expr")))); 187 ASSERT_TRUE(StmtMatch); 188 EXPECT_THAT(buildAddressOf(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"), 189 *StmtMatch->Result.Context), 190 ValueIs(std::string("this"))); 191 } 192 193 TEST(SourceCodeBuildersTest, BuildDereferencePointer) { 194 testBuilder(buildDereference, "S *x; x;", "*x"); 195 } 196 197 TEST(SourceCodeBuildersTest, BuildDereferenceValueAddress) { 198 testBuilder(buildDereference, "S x; &x;", "x"); 199 } 200 201 TEST(SourceCodeBuildersTest, BuildDereferenceValueAddressIgnoresParens) { 202 testBuilder(buildDereference, "S x; &(x);", "x"); 203 } 204 205 TEST(SourceCodeBuildersTest, BuildDereferenceBinaryOperation) { 206 testBuilder(buildDereference, "S *x; x + 1;", "*(x + 1)"); 207 } 208 209 TEST(SourceCodeBuildersTest, BuildDotValue) { 210 testBuilder(buildDot, "S x; x;", "x."); 211 } 212 213 TEST(SourceCodeBuildersTest, BuildDotPointerDereference) { 214 testBuilder(buildDot, "S *x; *x;", "x->"); 215 } 216 217 TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceIgnoresParens) { 218 testBuilder(buildDot, "S *x; *(x);", "x->"); 219 } 220 221 TEST(SourceCodeBuildersTest, BuildDotBinaryOperation) { 222 testBuilder(buildDot, "S x; x + x;", "(x + x)."); 223 } 224 225 TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceExprWithParens) { 226 testBuilder(buildDot, "S *x; *(x + 1);", "(x + 1)->"); 227 } 228 229 TEST(SourceCodeBuildersTest, BuildArrowPointer) { 230 testBuilder(buildArrow, "S *x; x;", "x->"); 231 } 232 233 TEST(SourceCodeBuildersTest, BuildArrowValueAddress) { 234 testBuilder(buildArrow, "S x; &x;", "x."); 235 } 236 237 TEST(SourceCodeBuildersTest, BuildArrowValueAddressIgnoresParens) { 238 testBuilder(buildArrow, "S x; &(x);", "x."); 239 } 240 241 TEST(SourceCodeBuildersTest, BuildArrowBinaryOperation) { 242 testBuilder(buildArrow, "S *x; x + 1;", "(x + 1)->"); 243 } 244 245 TEST(SourceCodeBuildersTest, BuildArrowValueAddressWithParens) { 246 testBuilder(buildArrow, "S x; &(true ? x : x);", "(true ? x : x)."); 247 } 248 } // namespace 249