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/AST/Type.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/ASTMatchers/ASTMatchers.h" 13 #include "clang/Tooling/Tooling.h" 14 #include "llvm/Testing/Support/SupportHelpers.h" 15 #include "gmock/gmock.h" 16 #include "gtest/gtest.h" 17 18 using namespace clang; 19 using namespace tooling; 20 using namespace ast_matchers; 21 22 namespace { 23 using MatchResult = MatchFinder::MatchResult; 24 using llvm::ValueIs; 25 26 // Create a valid translation unit from a statement. 27 static std::string wrapSnippet(StringRef StatementCode) { 28 return ("namespace std {\n" 29 "template <typename T> struct unique_ptr {\n" 30 " T* operator->() const;\n" 31 " T& operator*() const;\n" 32 "};\n" 33 "template <typename T> struct shared_ptr {\n" 34 " T* operator->() const;\n" 35 " T& operator*() const;\n" 36 "};\n" 37 "}\n" 38 "struct A { void super(); };\n" 39 "struct S : public A { S(); S(int); int Field; };\n" 40 "S operator+(const S &a, const S &b);\n" 41 "struct Smart {\n" 42 " S* operator->() const;\n" 43 " S& operator*() const;\n" 44 "};\n" 45 "auto test_snippet = []{" + 46 StatementCode + "};") 47 .str(); 48 } 49 50 static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) { 51 return varDecl(hasName("test_snippet"), 52 hasDescendant(compoundStmt(hasAnySubstatement(Matcher)))); 53 } 54 55 struct TestMatch { 56 // The AST unit from which `result` is built. We bundle it because it backs 57 // the result. Users are not expected to access it. 58 std::unique_ptr<ASTUnit> AstUnit; 59 // The result to use in the test. References `ast_unit`. 60 MatchResult Result; 61 }; 62 63 // Matches `Matcher` against the statement `StatementCode` and returns the 64 // result. Handles putting the statement inside a function and modifying the 65 // matcher correspondingly. `Matcher` should match one of the statements in 66 // `StatementCode` exactly -- that is, produce exactly one match. However, 67 // `StatementCode` may contain other statements not described by `Matcher`. 68 static llvm::Optional<TestMatch> matchStmt(StringRef StatementCode, 69 StatementMatcher Matcher) { 70 auto AstUnit = buildASTFromCodeWithArgs(wrapSnippet(StatementCode), 71 {"-Wno-unused-value"}); 72 if (AstUnit == nullptr) { 73 ADD_FAILURE() << "AST construction failed"; 74 return llvm::None; 75 } 76 ASTContext &Context = AstUnit->getASTContext(); 77 auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context); 78 // We expect a single, exact match for the statement. 79 if (Matches.size() != 1) { 80 ADD_FAILURE() << "Wrong number of matches: " << Matches.size(); 81 return llvm::None; 82 } 83 return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)}; 84 } 85 86 static void testPredicate(bool (*Pred)(const Expr &), StringRef Snippet, 87 bool Expected) { 88 auto StmtMatch = matchStmt(Snippet, expr().bind("expr")); 89 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; 90 EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"))) 91 << "Snippet: " << Snippet; 92 } 93 94 // Tests the predicate on the call argument, assuming `Snippet` is a function 95 // call. 96 static void testPredicateOnArg(bool (*Pred)(const Expr &), StringRef Snippet, 97 bool Expected) { 98 auto StmtMatch = matchStmt( 99 Snippet, expr(ignoringImplicit(callExpr(hasArgument( 100 0, ignoringElidableConstructorCall(expr().bind("arg"))))))); 101 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; 102 EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("arg"))) 103 << "Snippet: " << Snippet; 104 } 105 106 TEST(SourceCodeBuildersTest, needParensAfterUnaryOperator) { 107 testPredicate(needParensAfterUnaryOperator, "3 + 5;", true); 108 testPredicate(needParensAfterUnaryOperator, "true ? 3 : 5;", true); 109 testPredicate(needParensAfterUnaryOperator, "S(3) + S(5);", true); 110 111 testPredicate(needParensAfterUnaryOperator, "int x; x;", false); 112 testPredicate(needParensAfterUnaryOperator, "int(3.0);", false); 113 testPredicate(needParensAfterUnaryOperator, "void f(); f();", false); 114 testPredicate(needParensAfterUnaryOperator, "int a[3]; a[0];", false); 115 testPredicate(needParensAfterUnaryOperator, "S x; x.Field;", false); 116 testPredicate(needParensAfterUnaryOperator, "int x = 1; --x;", false); 117 testPredicate(needParensAfterUnaryOperator, "int x = 1; -x;", false); 118 } 119 120 TEST(SourceCodeBuildersTest, needParensAfterUnaryOperatorInImplicitConversion) { 121 // The binary operation will be embedded in various implicit 122 // expressions. Verify they are ignored. 123 testPredicateOnArg(needParensAfterUnaryOperator, "void f(S); f(3 + 5);", 124 true); 125 } 126 127 TEST(SourceCodeBuildersTest, mayEverNeedParens) { 128 testPredicate(mayEverNeedParens, "3 + 5;", true); 129 testPredicate(mayEverNeedParens, "true ? 3 : 5;", true); 130 testPredicate(mayEverNeedParens, "int x = 1; --x;", true); 131 testPredicate(mayEverNeedParens, "int x = 1; -x;", true); 132 133 testPredicate(mayEverNeedParens, "int x; x;", false); 134 testPredicate(mayEverNeedParens, "int(3.0);", false); 135 testPredicate(mayEverNeedParens, "void f(); f();", false); 136 testPredicate(mayEverNeedParens, "int a[3]; a[0];", false); 137 testPredicate(mayEverNeedParens, "S x; x.Field;", false); 138 } 139 140 TEST(SourceCodeBuildersTest, mayEverNeedParensInImplictConversion) { 141 // The binary operation will be embedded in various implicit 142 // expressions. Verify they are ignored. 143 testPredicateOnArg(mayEverNeedParens, "void f(S); f(3 + 5);", true); 144 } 145 146 TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeUniquePtr) { 147 std::string Snippet = "std::unique_ptr<int> P; P;"; 148 auto StmtMatch = 149 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("ty")))); 150 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; 151 EXPECT_TRUE( 152 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"), 153 *StmtMatch->Result.Context)) 154 << "Snippet: " << Snippet; 155 } 156 157 TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeSharedPtr) { 158 std::string Snippet = "std::shared_ptr<int> P; P;"; 159 auto StmtMatch = 160 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("ty")))); 161 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; 162 EXPECT_TRUE( 163 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"), 164 *StmtMatch->Result.Context)) 165 << "Snippet: " << Snippet; 166 } 167 168 TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeUnknownTypeFalse) { 169 std::string Snippet = "Smart P; P;"; 170 auto StmtMatch = 171 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("ty")))); 172 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; 173 EXPECT_FALSE( 174 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"), 175 *StmtMatch->Result.Context)) 176 << "Snippet: " << Snippet; 177 } 178 179 TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeNormalTypeFalse) { 180 std::string Snippet = "int *P; P;"; 181 auto StmtMatch = 182 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("ty")))); 183 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; 184 EXPECT_FALSE( 185 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"), 186 *StmtMatch->Result.Context)) 187 << "Snippet: " << Snippet; 188 } 189 190 static void testBuilder( 191 llvm::Optional<std::string> (*Builder)(const Expr &, const ASTContext &), 192 StringRef Snippet, StringRef Expected) { 193 auto StmtMatch = matchStmt(Snippet, expr().bind("expr")); 194 ASSERT_TRUE(StmtMatch); 195 EXPECT_THAT(Builder(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"), 196 *StmtMatch->Result.Context), 197 ValueIs(std::string(Expected))); 198 } 199 200 static void testBuildAccess(StringRef Snippet, StringRef Expected, 201 PLTClass C = PLTClass::Pointer) { 202 auto StmtMatch = matchStmt(Snippet, expr().bind("expr")); 203 ASSERT_TRUE(StmtMatch); 204 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"), 205 *StmtMatch->Result.Context, C), 206 ValueIs(std::string(Expected))); 207 } 208 209 TEST(SourceCodeBuildersTest, BuildParensUnaryOp) { 210 testBuilder(buildParens, "-4;", "(-4)"); 211 } 212 213 TEST(SourceCodeBuildersTest, BuildParensBinOp) { 214 testBuilder(buildParens, "4 + 4;", "(4 + 4)"); 215 } 216 217 TEST(SourceCodeBuildersTest, BuildParensValue) { 218 testBuilder(buildParens, "4;", "4"); 219 } 220 221 TEST(SourceCodeBuildersTest, BuildParensSubscript) { 222 testBuilder(buildParens, "int a[3]; a[0];", "a[0]"); 223 } 224 225 TEST(SourceCodeBuildersTest, BuildParensCall) { 226 testBuilder(buildParens, "int f(int); f(4);", "f(4)"); 227 } 228 229 TEST(SourceCodeBuildersTest, BuildAddressOfValue) { 230 testBuilder(buildAddressOf, "S x; x;", "&x"); 231 } 232 233 TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereference) { 234 testBuilder(buildAddressOf, "S *x; *x;", "x"); 235 } 236 237 TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereferenceIgnoresParens) { 238 testBuilder(buildAddressOf, "S *x; *(x);", "x"); 239 } 240 241 TEST(SourceCodeBuildersTest, BuildAddressOfBinaryOperation) { 242 testBuilder(buildAddressOf, "S x; x + x;", "&(x + x)"); 243 } 244 245 TEST(SourceCodeBuildersTest, BuildAddressOfImplicitThis) { 246 StringRef Snippet = R"cc( 247 struct Struct { 248 void foo() {} 249 void bar() { 250 foo(); 251 } 252 }; 253 )cc"; 254 auto StmtMatch = matchStmt( 255 Snippet, 256 cxxMemberCallExpr(onImplicitObjectArgument(cxxThisExpr().bind("expr")))); 257 ASSERT_TRUE(StmtMatch); 258 EXPECT_THAT(buildAddressOf(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"), 259 *StmtMatch->Result.Context), 260 ValueIs(std::string("this"))); 261 } 262 263 TEST(SourceCodeBuildersTest, BuildDereferencePointer) { 264 testBuilder(buildDereference, "S *x; x;", "*x"); 265 } 266 267 TEST(SourceCodeBuildersTest, BuildDereferenceValueAddress) { 268 testBuilder(buildDereference, "S x; &x;", "x"); 269 } 270 271 TEST(SourceCodeBuildersTest, BuildDereferenceValueAddressIgnoresParens) { 272 testBuilder(buildDereference, "S x; &(x);", "x"); 273 } 274 275 TEST(SourceCodeBuildersTest, BuildDereferenceBinaryOperation) { 276 testBuilder(buildDereference, "S *x; x + 1;", "*(x + 1)"); 277 } 278 279 TEST(SourceCodeBuildersTest, BuildDotValue) { 280 testBuilder(buildDot, "S x; x;", "x."); 281 } 282 283 TEST(SourceCodeBuildersTest, BuildDotPointerDereference) { 284 testBuilder(buildDot, "S *x; *x;", "x->"); 285 } 286 287 TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceIgnoresParens) { 288 testBuilder(buildDot, "S *x; *(x);", "x->"); 289 } 290 291 TEST(SourceCodeBuildersTest, BuildDotBinaryOperation) { 292 testBuilder(buildDot, "S x; x + x;", "(x + x)."); 293 } 294 295 TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceExprWithParens) { 296 testBuilder(buildDot, "S *x; *(x + 1);", "(x + 1)->"); 297 } 298 299 TEST(SourceCodeBuildersTest, BuildArrowPointer) { 300 testBuilder(buildArrow, "S *x; x;", "x->"); 301 } 302 303 TEST(SourceCodeBuildersTest, BuildArrowValueAddress) { 304 testBuilder(buildArrow, "S x; &x;", "x."); 305 } 306 307 TEST(SourceCodeBuildersTest, BuildArrowValueAddressIgnoresParens) { 308 testBuilder(buildArrow, "S x; &(x);", "x."); 309 } 310 311 TEST(SourceCodeBuildersTest, BuildArrowBinaryOperation) { 312 testBuilder(buildArrow, "S *x; x + 1;", "(x + 1)->"); 313 } 314 315 TEST(SourceCodeBuildersTest, BuildArrowValueAddressWithParens) { 316 testBuilder(buildArrow, "S x; &(true ? x : x);", "(true ? x : x)."); 317 } 318 319 TEST(SourceCodeBuildersTest, BuildAccessValue) { 320 testBuildAccess("S x; x;", "x."); 321 } 322 323 TEST(SourceCodeBuildersTest, BuildAccessPointerDereference) { 324 testBuildAccess("S *x; *x;", "x->"); 325 } 326 327 TEST(SourceCodeBuildersTest, BuildAccessPointerDereferenceIgnoresParens) { 328 testBuildAccess("S *x; *(x);", "x->"); 329 } 330 331 TEST(SourceCodeBuildersTest, BuildAccessValueBinaryOperation) { 332 testBuildAccess("S x; x + x;", "(x + x)."); 333 } 334 335 TEST(SourceCodeBuildersTest, BuildAccessPointerDereferenceExprWithParens) { 336 testBuildAccess("S *x; *(x + 1);", "(x + 1)->"); 337 } 338 339 TEST(SourceCodeBuildersTest, BuildAccessPointer) { 340 testBuildAccess("S *x; x;", "x->"); 341 } 342 343 TEST(SourceCodeBuildersTest, BuildAccessValueAddress) { 344 testBuildAccess("S x; &x;", "x."); 345 } 346 347 TEST(SourceCodeBuildersTest, BuildAccessValueAddressIgnoresParens) { 348 testBuildAccess("S x; &(x);", "x."); 349 } 350 351 TEST(SourceCodeBuildersTest, BuildAccessPointerBinaryOperation) { 352 testBuildAccess("S *x; x + 1;", "(x + 1)->"); 353 } 354 355 TEST(SourceCodeBuildersTest, BuildAccessValueAddressWithParens) { 356 testBuildAccess("S x; &(true ? x : x);", "(true ? x : x)."); 357 } 358 359 TEST(SourceCodeBuildersTest, BuildAccessSmartPointer) { 360 testBuildAccess("std::unique_ptr<int> x; x;", "x->"); 361 } 362 363 TEST(SourceCodeBuildersTest, BuildAccessSmartPointerAsValue) { 364 testBuildAccess("std::unique_ptr<int> x; x;", "x.", PLTClass::Value); 365 } 366 367 TEST(SourceCodeBuildersTest, BuildAccessSmartPointerDeref) { 368 testBuildAccess("std::unique_ptr<int> x; *x;", "x->"); 369 } 370 371 TEST(SourceCodeBuildersTest, BuildAccessSmartPointerDerefAsValue) { 372 testBuildAccess("std::unique_ptr<int> x; *x;", "(*x).", PLTClass::Value); 373 } 374 375 TEST(SourceCodeBuildersTest, BuildAccessSmartPointerMemberCall) { 376 StringRef Snippet = R"cc( 377 Smart x; 378 x->Field; 379 )cc"; 380 auto StmtMatch = 381 matchStmt(Snippet, memberExpr(hasObjectExpression(expr().bind("expr")))); 382 ASSERT_TRUE(StmtMatch); 383 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"), 384 *StmtMatch->Result.Context), 385 ValueIs(std::string("x->"))); 386 } 387 388 TEST(SourceCodeBuildersTest, BuildAccessIgnoreImplicit) { 389 StringRef Snippet = R"cc( 390 S x; 391 A *a; 392 a = &x; 393 )cc"; 394 auto StmtMatch = 395 matchStmt(Snippet, binaryOperator(isAssignmentOperator(), 396 hasRHS(expr().bind("expr")))); 397 ASSERT_TRUE(StmtMatch); 398 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"), 399 *StmtMatch->Result.Context), 400 ValueIs(std::string("x."))); 401 } 402 403 TEST(SourceCodeBuildersTest, BuildAccessImplicitThis) { 404 StringRef Snippet = R"cc( 405 struct Struct { 406 void foo() {} 407 void bar() { 408 foo(); 409 } 410 }; 411 )cc"; 412 auto StmtMatch = matchStmt( 413 Snippet, 414 cxxMemberCallExpr(onImplicitObjectArgument(cxxThisExpr().bind("expr")))); 415 ASSERT_TRUE(StmtMatch); 416 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"), 417 *StmtMatch->Result.Context), 418 ValueIs(std::string())); 419 } 420 421 TEST(SourceCodeBuildersTest, BuildAccessImplicitThisIgnoreImplicitCasts) { 422 StringRef Snippet = "struct B : public A { void f() { super(); } };"; 423 auto StmtMatch = matchStmt( 424 Snippet, 425 cxxMemberCallExpr(onImplicitObjectArgument(expr().bind("expr")))); 426 ASSERT_TRUE(StmtMatch); 427 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"), 428 *StmtMatch->Result.Context), 429 ValueIs(std::string())); 430 } 431 } // namespace 432